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]);

两句话的相对位置不影响正确性

posted @ 2026-03-26 17:27  江蝶  阅读(3)  评论(0)    收藏  举报