1. 程式人生 > >POJ 1390 Blocks(記憶化搜索+動態規劃)

POJ 1390 Blocks(記憶化搜索+動態規劃)

積木 輸入 segment table 單擊 初始 tin 其他 end

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(記憶化搜索+動態規劃)