poj1390 方块消除 dp

参考:徐源盛《对一类动态规划问题的研究》以及刘汝佳的黑书《算法艺术与信息学竞赛》

将方块序列,按颜色分成一段一段的,例如 1 1 1 1 1 3 2 2 1 1 1 可记为color[ 1 ] = 1; len[1 ] = 5; color[ 2 ] = 3; len[ 2 ] =1; color[ 3 ] = 2; len[ 3 ] = 2; color[ 4 ] = 1; len[ 4 ] = 3;   

(1) 如果直接消去第j个区域和未来会接到j后面的k块,那么f[i][j][k]=f[i][j-1][0]+(bj+k)*(bj+k)。
(2) 如果j与之前一起合并,假设与j颜色相同的是区域p,那么
f[i][j][k]=f[i][p][k+bj]+f[p+1][j-1][0](i<=p<j且color[ p ]==color[ j ])
f[i][j][k]为两者的最大值。 (注意:k为假设未来会有k个连在j的后面,并将这样的情况的值存储起来,如果未来真的发生这种情况,将这个值直接调用)
答案即是f[1][n][0]。

代码1:此代码感觉是有问题的,问题在注释中,但是可以AC

#include <iostream>
using namespace std;

#define MAX(a, b) a>b?a:b
#define N 202

int color[N], len[N], after[N];
int dp[N][N][N];

int main()
{
	int cas, m, n;
    int i, j, l, r, k, p, id;
    int seq[N], cnt;
	bool vis[N];
	
	cas = 1;
	scanf("%d", &m);
	while(m--)
	{
		scanf("%d", &n);
		scanf("%d", &seq[1]);
		id = cnt = 1;
        for(i = 2; i <= n; i++){
            scanf("%d", &seq[i]);
			if(seq[i] == seq[i-1])
				cnt++;
			else {
				color[id] = seq[i-1];
				len[id++] = cnt;
				cnt = 1;
			}
		}
		color[id] = seq[n];
		len[id++] = cnt;
		
		memset(vis, false, sizeof(vis));
		memset(after, -1, sizeof(after));
		for(i = 1; i < id-2; i++){  //after[i] 的值为下一段与第i段颜色相同的下标
			if(vis[i]) continue;
			int tmp = i;
			for(j = i+2; j < id; j++)
				if(color[tmp] == color[j]){
					after[tmp] = j;
					tmp = j;
					vis[j++] = true;
				}
		}
		
		memset(dp, 0, sizeof(dp));
		for(j = 1; j < id; j++){  //按长度遍历
			for(l = 1; l+j-1 < id; l++){  
				r = l+j-1;
				for(i = after[r], k = 0;; i = after[i]){ //k为未来接在r后的方块数
					dp[l][r][k] = dp[l][r-1][0]+(len[r]+k)*(len[r]+k);
					for(p = r-2; p >= l; p--)
						if(color[p] == color[r])
							dp[l][r][k] = MAX(dp[l][r][k], dp[l][p][k+len[r]]+dp[p+1][r-1][0]);
					if(i == -1) break;
					else k += len[i];
					//问题:k的取值为0,0+len[i1],..., 0+len[i1]+len[i2]+...一直加到某一段后面没有与之颜色相同的为止
					//(其中i1为r后面的与r颜色相同的第一段的下标,i2为i1后面的与i1颜色相同的第一段的下标)
					//觉得这样的写法是有问题的,因为k的取值还可能是 0不加len[i1]而直接加上len[i2]等,
				    //这样的k值我这样的写法是无法取到的
					
				}
			}
		}
		printf("Case %d: %d\n", cas++, dp[1][id-1][0]);
	}
	return 0;
}

代码2:这个代码没问题

#include <iostream>
using namespace std;

#define MAX(a, b) a>b?a:b
#define N 202

int color[N], len[N], after[N];
int dp[N][N][N];

int main()
{
	int cas, m, n;
    int i, j, l, r, k, p, id;
    int seq[N], cnt;
	bool vis[N];
	
	cas = 1;
	scanf("%d", &m);
	while(m--)
	{
		scanf("%d", &n);
		scanf("%d", &seq[1]);
		id = cnt = 1;
        for(i = 2; i <= n; i++){
            scanf("%d", &seq[i]);
			if(seq[i] == seq[i-1])
				cnt++;
			else {
				color[id] = seq[i-1];
				len[id++] = cnt;
				cnt = 1;
			}
		}
		color[id] = seq[n];
		len[id++] = cnt;
		
		memset(vis, false, sizeof(vis));
		for(i = id-1; i >= 1; i--){ /*此处after[i]记录i之后与i颜色相同的方块最多有多少个*/						                if(vis[i]) continue;  after[i] = 0;
			int tmp = i;
			for(j = i-2; j >= 1; j--)
				if(color[j] == color[tmp]){
					after[j] = after[tmp] + len[tmp];
					tmp = j;
					vis[j--] = true;
				}
		}
	
		memset(dp, 0, sizeof(dp));
		for(j = 1; j < id; j++){  //按长度遍历
			for(l = 1; l+j-1 < id; l++){  
				r = l+j-1;
				for(k = 0; k <= after[r]; k++){ //k为未来接在r后的方块数
					dp[l][r][k] = dp[l][r-1][0]+(len[r]+k)*(len[r]+k);
					for(p = r-2; p >= l; p--)
						if(color[p] == color[r])
							dp[l][r][k] = MAX(dp[l][r][k], dp[l][p][k+len[r]]+dp[p+1][r-1][0]);
				}
			}
		}
		printf("Case %d: %d\n", cas++, dp[1][id-1][0]);
	}
	system("pause");
	return 0;
}


 

你可能感兴趣的:(算法,System,存储)