题意:给一排方块,每个方块有一个颜色,每次可以选几个连续颜色相同方块消除,得分为方块数平方,求最大总得分。
按照一般的序列dp思路,dp[i][j]应当从dp[i][k]和dp[k][j]中转移(i<=k<=j),但本题中可能两边都剩下方块一起消,无法转移,状态也不好表示。
我预处理时先用分块的思想,把颜色相同的小块分为一个大块。
所以这个题以[i,j]这个区间中最后一个方块j怎样消除为决策,j可以直接就在当前消除,或者把中间的消除后,和左边一个颜色相同的大块拼起来(这个大块需要枚举).但是注意,和左边某个大块拼起来后,也不一定立即消除,有可能还和左边的左边的一个大块拼起来,所以还需要再开一维,dp[i][j][k]表示j的左边挂着k个和j一个颜色的小块。
看似状态n^3,转移最坏n,但是注意转移时枚举的是大块,一般数据会大大降低时间。还有dp[i][j][k]中的i,j都是大块的端点,每对i,j对应的k也不会很多,所以记搜实际状态数不多(数据组数<=15,980ms)
#include<iostream> #include<cstdio> #include<cstring> #define LL long long int n,A[500],B[500],L[500],R[500]; bool vis[210][210][210]; LL dp[210][210][210]; using namespace std; LL Max(LL a,LL b){ if (a>b) return a; else return b; } LL Sq(LL a){return a*a;} LL Dfs(int l,int r,int k){ LL &ans=dp[l][r][k]; if (vis[l][r][k]) return ans; vis[l][r][k]=1; if (l>r) return ans=0; ans=Dfs(l,L[B[r]]-1,0)+Sq(r-L[B[r]]+1+k); for (int i=B[r]-1;i>=B[l];i--) if (A[R[i]]==A[r]) ans=Max(ans,Dfs(l,R[i],k+r-L[B[r]]+1)+Dfs(R[i]+1,L[B[r]]-1,0)); return ans; } void InIt(){ memset(vis,0,sizeof(vis)); memset(B,0,sizeof(B)); memset(L,0,sizeof(L)); memset(R,0,sizeof(R)); memset(dp,0,sizeof(dp)); scanf("%d",&n); int i,cnt=0; for (i=1;i<=n;i++) scanf("%d",&A[i]); for (i=1;i<=n;i++) if (A[i]!=A[i-1]) B[i]=++cnt; else B[i]=cnt; for (i=1;i<=n;i++) if (!L[B[i]]) L[B[i]]=i; for (i=n;i>=1;i--) if (!R[B[i]]) R[B[i]]=i; } int main(){ int test_case; scanf("%d",&test_case); for (int i=1;i<=test_case;i++){ InIt(); printf("Case %d: %lld\n",i,Dfs(1,n,0)); } return 0; }