传送门:牛客
题目描述:
假设你有一条长度为5的木版,初始时没有涂过任何颜色。你希望把它的5个单位长度分别涂上红、绿、蓝、
绿、红色,用一个长度为5的字符串表示这个目标:RGBGR。 每次你可以把一段连续的木版涂成一个给定的
颜色,后涂的颜色覆盖先涂的颜色。
例如第一次把木版涂成RRRRR,第二次涂成RGGGR,第三次涂成RGBGR,达到目标。 用尽量少的涂色次
数达到目标。
输入:
RGBGR
输出:
3
首先对于这道题,我当时想到将原字符串进行一个压缩,因为涂色是一个范围形式的,所以我们可以直接将相邻的字符串进行压缩,获得一个新的相邻不重复的字符串
然后我随便想了想,发现这道题需要对左右端点的字符串是否相同进行分类(想到了回文子串),所以我当时想的是假设我们的左右端点的字符是一样的话显然我们可以直接去掉两端点了.但是显然这个思路是错误的,因为有可能将整个区间进行染色的过程中对中间区间有影响.有一个hack数据:ABACA
然后我仔细想了一下如何改变一下我的刚开始的思路,然后我就发现了,我们发现此时的区间 [ l , r ] [l,r] [l,r]两个端点的字符是一样的,所以假设我们将 [ l , r ] [l,r] [l,r]进行一个整体染色的话,其实我们的代价和对 [ l , r − 1 ] [l,r-1] [l,r−1]部分进行染色是一样的,因为我们刚开始是没哟任何颜色的,所以当我们涂下左端点的那一刻其实我们就可以顺带涂掉我们的右端点(此处如果有人有疑问为什么我们涂整个区间是最优的.因为当我们涂整个区间时,我们可以将除了两端点的剩下的区间当成一个没有染过色的区间,对于中间没有影响,但是如果我们没有同时涂的话,我们就需要花至少两次),所以此时就有dp方程:
接下来就是对两端点不同的区间进行转移了:
在提出转移方程之前,我们需要确定几个事实:
在基于以上事实之后我们自然而然的就有区间dp的想法了
下面是具体的代码部分:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int dp[200][200];
char a[maxn],s[maxn];
int main() {
cin>>a;s[0]=a[0];int lens=1;
for(int i=1;i<strlen(a);i++) {//简单的压缩,其实也可以不用压缩
if(a[i]==a[i-1]) continue;
s[lens++]=a[i];
}
memset(dp,0x3f,sizeof(dp));
for(int i=0;i<lens;i++) dp[i][i]=1;
for(int len=2;len<=lens;len++) {
for(int l=0;l+len-1<lens;l++) {
int r=l+len-1;
if(s[l]==s[r]){
dp[l][r]=dp[l+1][r];
continue;
}
for(int k=l;k<r;k++) {
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}
cout<<dp[0][lens-1]<<endl;
return 0;
}