:) Ch_someone

Ch_someone

涂色(CQOI2007)

——BZOJ1260_区间dp

Description

假设你有一条长度为5的木版,初始时没有涂过任何颜色。你希望把它的5个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为5的字符串表示这个目标:RGBGR。 每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,达到目标。 用尽量少的涂色次数达到目标。

Input

输入仅一行,包含一个长度为n的字符串,即涂色目标。字符串中的每个字符都是一个大写字母,不同的字母代表不同颜色,相同的字母代表相同颜色。

Output

仅一行,包含一个数,即最少的涂色次数。

样例输入1

AAAAA

样例输入2

RGBGR

样例输出1

1

样例输出2

3

HINT

40%的数据满足:1<=n<=10
100%的数据满足:1<=n<=50

Analysis

这种题,一个序列,求最小次数,能看出来是区间dp吧。
一般区间dp都是二维,我这里的i,j表示l,r,[i][j]表示(i,j)这个子序列。dp存答案。
整个区间从什么状态转移过来?
由短到长考虑,考虑长度为1的区间:dp[i][i]都是1,因为一个格子只用涂一次。
考虑长度为2的区间:dp[i][i+1],如果i和i+1颜色相同,只用涂一次,若颜色不同,就涂两次。
长度为3的区间,若1个格子与其他格子颜色不同,那就涂2次,全不同涂3次,全相同涂1次。
……
继续推下去,我们会发现,长度为n的区间总是由它的的子区间转移过来,而且若这个区间的两端颜色相同,就会少涂一次色。
找到这个规律之后,就能推出方程了。

\[dp[i][i+j] = min(dp[i][k],dp[k+1][i+j]) \]

当区间两端颜色相同时

\[dp[i][i+j] = dp[i][i+j] - 1 \]

代码

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf = 0x3f3f3f3f;
int dp[5005][5005];
char ss[5005];
int n;
int main()
{
	scanf("%s",ss+1);
	n = strlen(ss+1);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dp[i][j] = inf;
	for(int i=1;i<=n;i++)
		dp[i][i] = 1;
	for(int j=1;j<=n;j++)
	{
		for(int i=1;i<=n;i++)
		{
			for(int k=i;k<=i+j-1;k++)
			{
				dp[i][i+j] = min(dp[i][i+j] , dp[i][k] + dp[k+1][i+j]);
			}
			if(ss[i] == ss[i+j])
				dp[i][i+j]--;
		}
	}
	printf("%d",dp[1][n]);
	return 0;
}

posted on 2018-09-20 15:36  Ch_someone  阅读(292)  评论(0编辑  收藏  举报

导航