JSOI2007 祖碼Zuma

(同步個人博客 http://sxysxy.org/blogs/7 到csdn)

原題目見 http://www.lydsy.com/JudgeOnline/problem.php?id=1032

區間DP,我第一看見這題,好像是去年的時候了。。。。當時覺得超級高大上啊。。。

首先預處理出各個連續的顏色出現的位置和數量,可以表示成 (顏色,長度) 的形式,例如

顏色      1  1  2  1
         紅 紅 黃 紅       -> (1,2) (2,1) (1,1) 。預處理變成這樣的序列,記爲a,對這個序列a進行區間DP。
位置      1  2  3  4

然後就是套路,區間DP,外層循環枚舉區間長度,內層枚舉區間起點,再內層對這個區間進行最優化決策。令dp[i][j]表示從i開始,長度爲j的區間上,消掉珠子所用最少次數。

考慮如果一種顏色的珠子在一起只有1個的話,需要+2枚珠子,如果多於相同顏色珠子在一起,只需要在+1枚珠子。所以初始化條件:dp[i][1] = a(i)相同顏色的珠子的個數 == 1?2:1

轉移時要注意一個特殊情況,就是區間兩端的兩個珠子的顏色,如果相同,那麼考慮如果區間兩端的珠子的個數,如果爲2,那麼消掉區間除去兩端的中間部分後,還需要再+1顆珠子才能消掉整個區間的珠子。如果區間兩端相同顏色珠子個數和大於2,那麼中間消掉後,兩端珠子會自然一起消掉。

/**************************************************************
    Problem: 1032
    User: sxysxy
    Language: C++
    Result: Accepted
    Time:108 ms
    Memory:1812 kb
****************************************************************/

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
using namespace std;
#define MAXN 521
int main()
{
    int n;
    pair<int, int> a[MAXN];
    int dp[MAXN][MAXN];
    a[0].first = 233333333;
    int i, j, k, x;
    scanf("%d", &n);
    for(i = 1, j = 0; i <= n; i++)
    {
        scanf("%d", &x);
        if(x != a[j].first)
            a[++j].first = x;
        a[j].second++;   //預處理出各個顏色的起始端點和長度
    }
    n = j;
    memset(dp, 0x4e, sizeof(dp));
    for(i = 1; i <= n; i++)
        dp[i][1] = a[i].second==1?2:1;
    for(int len = 2; len <= n; len++)
    {
        for(int s = 1; s + len -1 <= n; s++)
        {
            if(a[s].first == a[s+len-1].first)
                dp[s][len] = dp[s+1][len-2]+(a[s].second+a[s+len-1].second==2?1:0);
            for(int k = 1; k < len; k++)
                dp[s][len] = min(dp[s][len], dp[s][k]+dp[s+k][len-k]);
        }
    }
    printf("%d\n", dp[1][n]);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章