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