-*CodeForces - 1312E Array Shrinking

Posted on 2021-12-27 17:16  Capterlliar  阅读(23)  评论(0编辑  收藏  举报

题意:给出一列数,每次可以挑两个相邻的合并,值在原来的基础上加一。问最后最少剩几个数。

解:和序列顺序有关,先套路地设dp[i]为到第i个为止最少的个数。考虑转移,每新加进来一个数,如果和前一个不同,那加一。如果和前一个相同,可以合并,也可以不合并,两种结果都保留。由于要看前一个是几,所以加一维,dp[i][j]表示到第i个,以j结尾的最短个数,一看ai取值1到1000,很好。于是得到以下错误代码:

for(int i=1;i<=n;i++){
        for(int j=1;j<=5000;j++)
            dp[i][a[i]]=min(dp[i][a[i]],dp[i-1][j]+1);
        int t=a[i],cnt=i;
        while (dp[cnt-1][t]!=0&&dp[cnt-1][t]!=inf&&cnt>0){
            dp[i][t+1]=min(dp[i][t+1],dp[cnt-1][t]);
            cnt--;
            t++;
        }
    }

  WA在第五个点。绷不住去cf看了一眼,哦每次加进来一个就从前面的取最小,这样会吞掉一些情况,使得本来和后面合并能变大然后消掉前面的消不掉了。

样例:

15
7 7 5 5 6 6 6 8 7 7 7 5 5 6 10

正解:既然有的和后面先合并比较好,那先处理每个区间能合并出的数字,也就是区间dp。这玩意是有模板的,先记住吧反正数数我也数不来。注意这里不是处理每个区间最少有几个数,而是这个区间能否合并为一个数,如果能,是几。也就是说,原来只能看能否和前一个合并,现在可以把前面的每个区间都试一遍,不会破坏信息。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define maxx 1000005
#define eps 0.00000001
#define inf 0x3f3f3f3f
#define mod 1000000007
//#define int long long
int n;
int dp1[505][505]={0},dp2[505]={0};
int a[505];
signed main() {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
        dp1[i][i]=a[i];
    for(int len=1;len<=n;len++){
        for(int i=1;i<=n-len;i++){
            int j=len+i;
            for(int k=i;k<j;k++)
                if(dp1[i][k]==dp1[k+1][j]&&dp1[i][k]!=0)
                    dp1[i][j]=dp1[i][k]+1;
        }
    }
    dp2[0]=0;
    for(int i=1;i<=n;i++){
        dp2[i]=dp2[i-1]+1;
        for(int j=1;j<=i;j++)
            if(dp1[j][i]!=0)
                dp2[i]=min(dp2[i],dp2[j-1]+1);
    }
    printf("%d\n",dp2[n]);
    return 0;
}
View Code