POJ 1390 Blocks(記憶化搜索+動態規劃)
POJ 1390 Blocks
砌塊時限:5000 MS | 內存限制:65536K | |
提交材料共計: 6204 | 接受: 2563 |
描述
你們中的一些人可能玩過一個叫做“積木”的遊戲。一行有n個塊,每個盒子都有一個顏色。這是一個例子:金,銀,銅,金。相應的圖片如下:
圖1
如果一些相鄰的盒子都是相同的顏色,並且它左邊的盒子(如果它存在)和它的右邊的盒子(如果它存在)都是其他顏色的,我們稱它為“盒子段”。有四個盒子段。那就是:金,銀,銅,金。片段中分別有1,4,3,1方框。
每次您可以單擊一個框,然後包含該框的整個段消失。如果這段是由k個方框組成的,你會得到k*k點。例如,如果你點擊一個銀盒子,銀段消失了,你得到4*4=16點。
現在讓我們看看下面的圖片:
圖2
第一個是最優的。
在這個遊戲的初始狀態下,找出你能得到的最高分數。
輸入
第一行包含測試數t(1<=t<=15)。每個案例包含兩行。第一行包含整數n(1<=n<=200),即框數。第二行包含n個整數,表示每個框的顏色。整數在1~n的範圍內。輸出量
對於每個測試用例,打印用例編號和最高可能的分數。樣本輸入
2 9 1 2 2 2 2 3 3 3 1 1 1
樣本輸出
Case 1: 29 Case 2: 1
解題思路:
將連續的若幹個方塊作為一個“大塊”(box_segment) 考慮,假設開始一共有 n個“大塊”,編號0到n-1 第i個大塊的顏色是 color[i],包含的方塊數目,即長度,是len[i]
用click_box(i,j)表示從大塊i到大塊j這一段消除後所能 得到的最高分,則整個問題就是: click_box(0,n-1)。
要求click_box(i,j)時,考慮最右邊的大塊j,對它有兩種處理方式,要取其優者:
1) 直接消除它,此時能得到最高分就是: click_box(i,j-1) + len[j]*len[j]
2) 期待以後它能和左邊的某個同色大塊合並,考慮和左邊的某個同色大塊合並:
左邊的同色大塊可能有很多個,到底和哪個合並最 好,不知道,只能枚舉。假設大塊j和左邊的大塊 k(i<=k<j-1) 合並,此時能得到的最高分是多少呢?
是不是: click_box(i,k-1) + click_box(k+1,j-1) + (len[k]+len[j])
不對! 因為將大塊k和大塊j合並後,形成的新大塊會在最右邊。但直接將其消去,未必是最好的,也許它還應該和左邊的同色大塊合並,才更好
那麽上面的dp不可用,需要改變問題的形式
__________________________________________________________________
click_box(i,j,ex_len) 表示: 大塊 j 的右邊已經有一個長度為ex_len的大塊(該大塊可能是在合並過程中形成的),且 j 的顏色和ex_len相同,在此情況下所能得到的最高分 。
於是整個問題就是求:click_box(0,n-1,0)
求click_box(i,j,ex_len)時,有兩種處理方法取最優者,假設j和ex_len合並後的大塊稱作 Q
1) 將Q直接消除,這種做法能得到的最高分就是: click_box(i,j-1,0) + (len[j]+ex_len)2
2) 期待Q以後能和左邊的某個同色大塊合並。需要枚舉可能和Q 合並的大塊。假設讓大塊k和Q合並,則此時能得到的最大分數是:
click_box(i,k,len[j]+ex_len) + click_box(k+1,j-1,0)
click_box(i,j,ex_len) 遞歸的終止條件: i == j
代碼:
#include<iostream> #include<cstring> using namespace std; #define N 200 + 5 int dp[N][N][N]; struct segMent { int len; int color; }; segMent segNum[N]; int clickBox(int i, int j, int len) { if(dp[i][j][len] != -1) return dp[i][j][len]; int result = (segNum[j].len + len)*(segNum[j].len + len); if(i == j) return result; result += clickBox(i, j-1, 0); for(int k = i; k < j; k++) { if(segNum[k].color != segNum[j].color) continue; int r = clickBox(k+1, j-1, 0) + clickBox(i, k, segNum[j].len + len); result = max(result, r); } dp[i][j][len] = result; return result; } int main() { int T; cin >> T; for(int t = 1; t <= T; t++) { int n; cin >> n; int last = -1; int count = -1; memset(dp, -1, sizeof(dp)); for(int i = 0; i < n; i++) { int v; cin >> v; if(v != last) { count++; segNum[count].len = 1; segNum[count].color = v; last = v; } else segNum[count].len++; } cout << "Case " << t << ": " << clickBox(0, count, 0) << endl; } return 0; }
POJ 1390 Blocks(記憶化搜索+動態規劃)