1. 程式人生 > >POJ 1390 Blocks (區間DP)

POJ 1390 Blocks (區間DP)

-- 收益 cst 記錄 true stream ++ while 合並

題意:給你一個顏色塊序列,每次你可以刪除一些相同顏色並且相鄰的顏色塊,並獲得刪除數目平方的收益,現在給你一個顏色塊序列,問收益最大是多少?

思路:首先我們把每個本來相鄰且顏色相同的塊合並成一個大塊。我們可以分區間處理,然後嘗試合並區間。然而我們發現這非常的困難,因為再加入一個新的顏色塊之後,獲得子區間最優值的操作次序對當前區間可能不是最優的了。比如後面和前面有顏色相同的兩組顏色塊,等他們之間的所有顏色塊合並之後他們再合並可能創造出更有解。因此,我們不妨多加一維,記錄一下區間後面有多少個顏色塊與當前區間的最後一個顏色塊顏色相同。設dp[i][j][k]代表合並區間[i, j]內的顏色塊,並且有k個顏色塊與j顏色塊相同。這時我們可以執行2種策略:

1:先把第j個顏色塊和後面的k個顏色塊合並了。

2:先不急著合並,看一看[i, j - 1]中有沒有與j顏色相同的,如果有(假設這個和j顏色相同的顏色塊是p),那麽先把[p, j - 1]合並了。

代碼:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int dp[210][210][210];

int len[210], c[210], tot;

int solve(int l, int r, int k) {
	if(dp[l][r][k])
		return dp[l][r][k];
	if(l == r) return (len[r] + k) * (len[r] + k);
	dp[l][r][k] = solve(l, r - 1, 0) + (len[r] + k) * (len[r] + k);
	for (int i = l; i < r; i++) {
		if(c[i] == c[r]) {
			dp[l][r][k] = max(dp[l][r][k], solve(l, i, len[r] + k) + solve(i + 1, r - 1, 0));
		}
	}
	return dp[l][r][k];
}

int main() {
	int T, n, x, kase = 0;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		int now = -1;
		int tot = 0;
		memset(len , 0, sizeof(len));
		memset(dp, 0, sizeof(dp));
		for (int i = 1; i <= n; i++) {
			scanf("%d", &x);
			if(x == now) {
				len[tot]++;
			} else {
				c[++tot] = x;
				len[tot]++;
				now = x;
			}
		}
		printf("Case %d: %d\n", ++kase, solve(1, tot, 0));
	} 
}

  

POJ 1390 Blocks (區間DP)