(同步個人博客 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;
}