Week2 DP题解
Week2 DP题解
Dynasty Puzzles
题意
给出n段字符串,进行字符串的拼接,拼接时将新串接到原串后面,要求新串的尾字母和原串的头字母相同,新串的尾字母和原串的头字母相同
思路
设置状态dp[26][26],dp[i][i]是以i开头,结尾的最长字符串长度,答案就是\(max(dp[i][i]),i\in [0,25]\).考虑前i个字符串,对每个字符串进行可能的拼接,维护最大值即可
代码
void solve(){
int n;cin>>n;
string s;
int dp[26][26];
memset(dp,0,sizeof(dp));
while(n--){
cin>>s;
int len=s.size();
int l=s[0]-'a';
int r=s[len-1]-'a';
for(int i=0;i<26;i++){
if(dp[i][l]){
dp[i][r]=max(dp[i][r],dp[i][l]+len);
}
}dp[l][r]=max(dp[l][r],len);
}int mx=0;
for(int i=0;i<26;i++) mx=max(mx,dp[i][i]);
cout<<mx<<endl;
}
Boredom
题意
给定数组n,每次在删除\(a_k\)的时候必须同时删除数组里面有的\(a_k+1\)和\(a_k-1\),获得\(a_k\)点分数,最大化总分数.
思路
值域考虑i从2到\(max\ a_i\),对于每个位置,答案要么是删掉前一个数获得sco[i-1],要么是删掉i-2获得sco[i]+sco[i],递推即可
#include<iostream>
#include<algorithm>
using namespace std;
long long a[100005];
int main(){
int i,n,k;
cin>>n;
for(i=0;i<n;i++){
cin>>k;
a[k]+=k;
}
for(i=2;i<100001;i++){
a[i]=max(a[i]+a[i-2],a[i-1]);
}
cout<<a[i-1];
return 0;
}
Multiplicity
题意
给定数组a[],求使得数组的子序列中良好数组的个数.如果数组 \(b_1, b_2, \ldots, b_k\) 不为空,并且对于每个 \(i\) ( \(1 \le i \le k\) ) \(b_i\) 都能被 \(i\) 整除,则该数组被称为良好数组。
思路
设置dp[\(max \ a_i\)],dp[i]表示长度为i的良好数组的个数.对于a[],我们枚举a[i]的因子,然后从小到大更新dp[](防止因子被重复添加),最后进行sum即可
void solve(){
int n;
cin>>n;ll ans=0;ll dp[MAXN]={0};vector<ll>a(n+1);
for(int i=1;i<=n;i++)cin>>a[i];
dp[0]=1;
for(int i=1;i<=n;i++){
stack<int>s;
queue<int>q;
for(int j=1;j*j<=a[i];j++){
if(a[i]%j==0){
s.push(j);if(a[i]/j!=j) q.push(a[i]/j);
}
}
while(!q.empty()){
int x=q.front();q.pop();
dp[x]=(dp[x]+dp[x-1])%MOD;
}
while(!s.empty()){
int x=s.top();s.pop();
dp[x]=(dp[x]+dp[x-1])%MOD;
}
}
for(int i=1;i<=n;i++) ans=(ans+dp[i])%MOD;
cout<<ans<<endl;
}
Jongmah
题意
给定一个数组,在里面选取牌组(连续的三个数,A型或者相同的三个数字,B型号),求牌组的最大数目
思路
在值域里面枚举,可以从小到大枚举也可以从大到小枚举,我这里从大到小. 由于三个A型可以完全换成三个B型,所以相同的A型最多不超过2个,于是我们将dp数组定义为dp[m][5][3],dp[i][j][k]表示从i到m组成的牌组数(并且在i之前借了j个i,k个i+1),在保证牌数还有的情况下进行状态转移
int my=num[i]-j-k-l,nxt=num[i+1]-k-l,nnxt=num[i+2]-l;
if(min({my,nxt,nnxt})>=0)
dp[i][j+k][k]=max(dp[i+1][k+l][l]+my/3+l,dp[i][j+k][k]);
const int INF=1e18,N=1e6+6,MOD=998244353;
int dp[N][5][3],num[N];
void solve(){
int n,m;
cin>>n>>m;
int t;
for(int i=0;i<n;i++){
cin>>t;
num[t]++;
}
for(int i=m;i>=1;i--)
for(int j=0;j<=2;j++)
for(int k=0;k<=2;k++)
for(int l=0;l<=2;l++){
int my=num[i]-j-k-l,nxt=num[i+1]-k-l,nnxt=num[i+2]-l;
if(min({my,nxt,nnxt})>=0)
dp[i][j+k][k]=max(dp[i+1][k+l][l]+my/3+l,dp[i][j+k][k]);
}
cout<<dp[1][0][0]<<endl;
}
Array Shaking
题意
在给定的数组里面进行抖动操作,每次抖动都可以把两个相邻的数字换成其中一个(2变1),求最终的数组最短可以达到多短.
思路
使用区间DP.dp[l][r]表示从l到r最短可以达到多短,v[l][r]表示区间l到r只有一个值是这个值是多少.状态转移方程如下
if(dp[l][mid]==dp[mid+1][r]&&dp[l][mid]==1&&v[l][mid]==v[mid+1][r]){
v[l][r]=v[l][mid]+1;
dp[l][r]=1;
}
dp[l][r]=min(dp[l][r],dp[l][mid]+dp[mid+1][r]);
两句话的相对位置不影响正确性

浙公网安备 33010602011771号