~~~~~ P4170 [CQOI2007] 涂色 ~~~~~ 总题单链接
思路
~~~~~ 这道题的关键在于:对于两次涂色,要么不交,要么包含且端点不重。
~~~~~ 证明:如果相交但不包含,缩短其中一个区间就可以变为不交的情况。
~~~~~ 设 d p [ L ] [ R ] dp[L][R] dp[L][R] 表示将区间 [ L , R ] [L,R] [L,R] 涂色的最小次数,考虑两种情况。
~~~~~ 第一种: s [ L ] = s [ R ] s[L]=s[R] s[L]=s[R] ,根据上面的性质,区间 [ L , R − 1 ] [L,R-1] [L,R−1] 的第一次涂色一定包含了 L L L ,将这一次涂色的右端点改成 R R R 即可,所以 d p [ L ] [ R ] = d p [ L ] [ R − 1 ] dp[L][R]=dp[L][R-1] dp[L][R]=dp[L][R−1]。同理,也可以 d p [ L ] [ R ] = d p [ L + 1 ] [ R ] dp[L][R]=dp[L+1][R] dp[L][R]=dp[L+1][R]。
~~~~~ 第二种: s [ L ] ≠ s [ R ] s[L]\neq s[R] s[L]=s[R] ,还是根据上面的性质,一定存在一个 i i i 使得没有区间跨过 i i i,枚举 i i i 即可。 d p [ L ] [ R ] = min i < R i = L ( d p [ L ] [ i ] + d p [ i + 1 ] [ R ] ) dp[L][R]=\underset{i=L}{\overset{i<R}{\min}}(dp[L][i]+dp[i+1][R]) dp[L][R]=i=Lmini<R(dp[L][i]+dp[i+1][R])。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
string s;
ll n,dp[55][55];
signed main(){
ios::sync_with_stdio(false);
cin>>s;s='.'+s;
n=s.size()-1;
memset(dp,0x3f,sizeof(dp));
for(ll i=1;i<=n;i++)dp[i][i]=1;
for(ll len=2;len<=n;len++)
for(ll L=1;L+len-1<=n;L++){
ll R=L+len-1;
if(s[L]==s[R])dp[L][R]=dp[L][R-1];
else{
for(ll j=L;j<R;j++)
dp[L][R]=min(dp[L][R],dp[L][j]+dp[j+1][R]);
}
}
cout<<dp[1][n];
return 0;
}