此题请参见黑书123页。
题目的方块用color 和len表示。
用f [ i ] [ j ] [ k ] 表示把(color [ i ] , len [ i ] ), (color [ i+1 ] , len [ i+1 ] ) ,……, (color [ j-1 ] , len [ j-1 ] ),(color [ j ] , len [ j ]+k )合并的最大得分
考虑(color [ j ] , len [ j ]+k )这一段要不马上消去 要不和前面的一起消去
如果马上消去:f【i】【j-1】【0】+(len【j】+k)^2
如果和前面一起消去 那么前面有的段数可以有P位 那么得分可以有P种情况
f【i】【p】【k+len[j]】+f【p+1】【j-1】【0】;(color【p】=color【j】 i<=p<j)
所以f 【i】【j】【k】=两个的最大值. 边界条件是f【i】【i-1】【0】=0
那么为什么与前面一起消去的时候会是那个式子呢:
中间P区域相当于过度区域,那么就是I->P P>J,那么P后面的块数就应当加上当前的J的长度,因为J也算在来P的后面,然后加上从P+1到J-1后面长度为0部分
个人觉得这个算法可以理解为最长路的思路。
状态本来用dp【i】【j】表示i到j的最大值就可以了。由于这里要有中间变量。即通过中间p中转后到达j使得答案最大。那么中间P的状态有很多种。所以我们在这里就需要将其状态记录下来不然用不到子结构(或者用四层循环来模拟中间过程那么时间复杂度就高了)所以我们通过加一维状态保存中间的P状态 那么这样就可以用到最优子结构了。相当好的一道题目啊。
我跑的时间是600多MS 但是网上很多是1700++ 那是用递归的写法的 而且没有优化。但是写出来很好看很简短
递归的:http://blog.youkuaiyun.com/binwin20/article/details/7989213
我的代码中心思想和他一样 但是我是用四层循环处理的各个条件。貌似快些
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 209;
int dp[N][N][N];
int re[N];
int dfs(int l,int r,int k)
{
if(l>r) return 0;
if(dp[l][r][k]) return dp[l][r][k];
dp[l][r][k] = dfs(l,r-1,0)+(1+k)*(1+k);
for(int i=r-1;i>=l;i--)
if(re[r]==re[i]){
dp[l][r][k] = max(dp[l][r][k],dfs(l,i,k+1)+dfs(i+1,r-1,0));
}
return dp[l][r][k];
}
int main()
{
int cas,T=1,n;
scanf("%d",&cas);
while(cas--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&re[i]);
memset(dp,0,sizeof(dp));
printf("Case %d: %d\n",T++,dfs(1,n,0));
}
return 0;
}
pascal的带解析:http://hi.baidu.com/agosits/item/a84fdb2329e057e650fd87e8解释了下最优子结构怎么推的
网上还有一个神代码貌似是预处理来一遍 跑出来只有200不到
http://blog.sina.com.cn/s/blog_934b8a370100ygmo.html 可以膜拜下。