【题目大意】
有$n$个方块,每个方块有一个颜色。现在要消除这些方块,一段颜色相同的$k$个方块消除后的得分为$k^2$,求消除所有方块后的最大得分。
【思路分析】
这题还是很容易想到DP的?
设$f[i][j][k]$表示当前处理到$[i,j]$,右边还有$k$个和$j$颜色相同的方块,我们考虑分情况转移:
1.把$j$和后面$k$个同色方块一起消掉,$s[j]$表示$j$右边和$j$颜色相同的方块总数
$$f[i][j][k]=max\{f[i][j-1][0]+(k+1)^2\}(k\in[0,s[j]])$$
2.在$[i,j]$之间找一个和$j$颜色相同的方块$p$,把$[p+1,j-1]$之间的方块消掉,然后把$p$和$j$以及$j$后面的$k$个颜色相同的方块一起消掉,$c[j]$代表$j$的颜色
$$f[i][j][k]=max\{f[i][p][k+1]+f[p+1][j-1][0]\}(k\in[0,s[j]],p\in[i,j-1]\&\&c[p]=c[j])$$
然后就是要注意一下循环的顺序,一定是从后往前转移!!!因为如果从前往后转移即$i,j$从小到大枚举,那么在求$f[i][j][k]$的时候还没有求出$f[p+1][j-1][0]$的值,这样是错的!
最后的答案是$f[1][n][0]$
【代码实现】
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #define g() getchar() 8 #define rg register 9 #define go(i,a,b) for(rg int i=a;i<=b;i++) 10 #define back(i,a,b) for(rg int i=a;i>=b;i--) 11 #define db double 12 #define ll long long 13 #define il inline 14 #define pf printf 15 #define mem(a,b) memset(a,b,sizeof(a)) 16 using namespace std; 17 int fr(){ 18 int w=0,q=1; 19 char ch=g(); 20 while(ch<'0'||ch>'9'){ 21 if(ch=='-') q=-1; 22 ch=g(); 23 } 24 while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=g(); 25 return w*q; 26 } 27 const int N=202; 28 int T,n,c[N],s[N],f[N][N][N]; 29 int main(){ 30 //freopen("","r",stdin); 31 //freopen("","w",stdout); 32 T=fr(); 33 go(t,1,T){ 34 n=fr(); 35 mem(f,0);mem(s,0); 36 go(i,1,n) c[i]=fr(); 37 back(i,n-1,1) go(j,i+1,n) if(c[i]==c[j]) s[i]++; 38 back(i,n,1) go(j,i,n){//i要从大到小枚举! 39 go(p,i,j-1) 40 if(c[p]==c[j]) 41 go(k,0,s[j]) f[i][j][k]=max(f[i][j][k],f[i][p][k+1]+f[p+1][j-1][0]); 42 go(k,0,s[j]) f[i][j][k]=max(f[i][j][k],f[i][j-1][0]+(k+1)*(k+1)); 43 } 44 pf("Case %d: %d\n",t,f[1][n][0]); 45 } 46 return 0; 47 }代码戳这里