[CQOI2007] 涂色
仔细想想其实没有dX说的那么夸张。
考虑一个结论:一定存在一种最优方案使得使得任意一次染色的区间一定是完全包含之前某一次染色区间或者与之前某一次染色区间完全不交且不与之前所有染色区间相交。
简单证明,如果我们当前的染色方案与之前某一次相交,那么我们完全可以缩短当前染色区间使得不交。
我们发现这个性质能够把染色的过程抽象为类似线段树的结构,即线段与线段之间只存在包含和并列的关系,一个大区间的答案可以由其中的小区间合并而来,于是我们可以想到用区间 DP 解决这个问题。
这样我们设 \(dp_{l,r}\) 表示染色区间为 \(l,r\) 时的答案,对上述两种情况分别考虑:
-
完全包含。此时一定有 \(s_l=s_r\),那么 \(dp_{l,r}\) 可以由 \(dp_{l,r-1}\) 和 \(dp_{l+1,r}\) 转移过来。具体的,比如对于
ABA
,它可以由AB
直接转移,即第一次染色时可以向右端点多染一次。这里可能会有疑问,也许这种染色方式会不满足上文条件。但其实不要紧,因为就算不满足我们也可以通过前文的转化方式使其满足条件。等价于转移时不需要完全满足上文条件。 -
完全不相交。我们在这里其实只需要考虑紧挨的情况,即类似
AABB
,因为不紧挨的情况可以由多个紧挨的情况转移过来。所以只需要枚举断点 \(k\),合并累加两段答案即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int w=1,s=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
while(isdigit(ch)){s=s*10+(ch-'0');ch=getchar();}
return w*s;
}
const int mod=998244353;
const int maxn=600;
const int inf=3e13+7;
char s[maxn];
int n;
int dp[maxn][maxn];
signed main()
{
#ifdef Lydic
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
// #else
// freopen("Stone.in","r",stdin);
// freopen("Stone.out","w",stdout);
#endif
scanf("%s",s+1);
n=strlen(s+1);
memset(dp,0x3f,sizeof dp);
for(int i=1;i<=n;i++)dp[i][i]=1;
for(int len=2;len<=n;len++)
{
for(int l=1;l<=n-len+1;l++)
{
int r=l+len-1;
if(s[l]==s[r])dp[l][r]=min(dp[l][r-1],dp[l+1][r]);
else
{
for(int k=l;k<=r;k++)
dp[l][r]=min(dp[l][r],dp[l][k]+dp[k+1][r]);
}
}
}
cout<<dp[1][n];
return 0;
}