洛谷P4170 [CQOI2007]涂色

本文介绍了一种解决特定涂色问题的算法,通过动态规划的方法找出将一定长的木板涂上指定颜色序列所需的最少涂色次数。文章详细解释了状态转移方程,并提供了一段实现该算法的C++代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

假设你有一条长度为5的木版,初始时没有涂过任何颜色。你希望把它的5个单位长度分别涂上红、绿、蓝、绿、红色,用一个长度为5的字符串表示这个目标:RGBGR。

每次你可以把一段连续的木版涂成一个给定的颜色,后涂的颜色覆盖先涂的颜色。例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,达到目标。

用尽量少的涂色次数达到目标。

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 
AAAAA
输出样例#1: 
1
输入样例#2: 
RGBGR
输出样例#2: 
3

说明

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

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


i==j时,子串明显只需要涂色一次,于是f[i][j]=1

i!=js[i]==s[j]时,可以想到只需要在首次涂色时多涂一格即可,于是f[i][j]=min(f[i][j-1],f[i+1][j])

i!=js[i]!=s[j]时,我们需要考虑将子串断成两部分来涂色,于是需要枚举子串的断点,设断点为k,那么f[i][j]=min(f[i][j],f[i][k]+f[k+1][j])

总结一下就是:

f_{i,j}=1\ (i==j)fi,j=1 (i==j)

f_{i,j}=\min(f_{i,j-1},f_{i+1,j})\ (i!=j,\ s_i==s_j)fi,j=min(fi,j1,fi+1,j) (i!=j, si==sj)

f_{i,j}=\min(f_{i,j},f_{i,k}+f_{k+1,j})\ (i!=j,\ s_i!=s_j,\ i\le k<j)fi,j=min(fi,j,fi,k+fk+1,j) (i!=j, si!=sj, ik<j)

由于f[i][j]的定义,我们可以知道f[1][n]即为答案。

附代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 60
using namespace std;
int n,dp[MAXN][MAXN];
char s[MAXN];
inline int read(){
	int date=0,w=1;char c=0;
	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
	return date*w;
}
int main(){
	scanf("%s",s+1);
	n=strlen(s+1);
	memset(dp,127,sizeof(dp));
	for(int i=1;i<=n;i++)dp[i][i]=1;
	for(int i=1;i<n;i++)
	for(int j=1,k=i+1;k<=n;j++,k++){
		if(s[j]==s[k])dp[j][k]=min(dp[j+1][k],dp[j][k-1]);
		else
		for(int l=j;l<k;l++)dp[j][k]=min(dp[j][k],dp[j][l]+dp[l+1][k]);
	}
	printf("%d\n",dp[1][n]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值