题意:给出一列数,每次可以挑两个相邻的合并,值在原来的基础上加一。问最后最少剩几个数。
解:和序列顺序有关,先套路地设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; }