Description
Input
Output
Sample Input
2 9 1 2 2 2 2 3 3 3 1 1 1
Sample Output
Case 1: 29 Case 2: 1
思路:
1. 将颜色相同又邻接的方块合并到一起, 成为一个 segment. 由得分的规则知, 一个 segment 同时消除一定比 segment 内部分成几个部分再消除得分高
2. 设 i 表示 segment 的 index. 那么 color[i], len[i] 分别代表第 i 个 segment 的颜色和长度
3. dp[i, j, k] 表示从第 i 到 第 j 个 segment, 并且第 j 个 segment 后面还有 k 个不与 j 相邻但颜色与 j 相同的方块数
4. 当第 j 个 segment 与后面 k 个结合时, 那dp[i, j, k] = dp[i, j-1, 0] + (len[j]+k)^2
5. 当第 j 个 segment 与前面的某段合并时, dp[i, j, k] = max(dp[i, j, k], dp[i, s, k+len[j]]+dp[s+1, j-1, 0])
相当于先消除 [s+1, j-1] 这些方块
6. 由 (5) 的状态转移方程可以看出, 有 max, 有枚举, 这是一个典型的记忆化搜索过程
update 2014年3月15日15:31:20
1.思考时应当考虑从后向前推
代码:
#include <iostream> using namespace std; const int MAXN = 210; int testCase, N; int color[MAXN]; int len[MAXN]; int block[MAXN]; int dp[MAXN][MAXN][MAXN]; int solve_dp(int l, int r, int k) { if(dp[l][r][k]) return dp[l][r][k]; if(l == r) return dp[l][r][k] =(k+len[r])*(k+len[r]); // 直接消除 //cout << solve_dp(l, r-1, 0) + (k+len[r])*(k+len[r]) << endl; dp[l][r][k] = solve_dp(l, r-1, 0) + (k+len[r])*(k+len[r]); // 先消除中间的, 合并 color[r], 在消除颜色为 r 的 for(int i = l; i < r; i++) { if(color[i] == color[r]) dp[l][r][k] = max(dp[l][r][k], solve_dp(l, i, k+len[r]) + solve_dp(i+1, r-1, 0)); } return dp[l][r][k]; } int main() { freopen("E:\\Copy\\test\\in.txt", "r", stdin); cin >> testCase; int caseTh = 0; while(testCase--) { caseTh++; cin >> N; for(int i = 0; i < N ; i ++) { cin >> block[i]; } color[0] = block[0]; len[0] = 1; int cursor = 0; for(int i = 1; i < N; i ++) { if(block[i] == block[i-1]) { len[cursor]++; }else{ cursor ++; color[cursor] = block[i]; len[cursor] = 1; } } memset(dp, 0, sizeof(dp)); printf("Case %d: %d\n", caseTh, solve_dp(0, cursor, 0)); } return 0; }