bzoj1260: [CQOI2007]涂色paint
区间dp。
用f[l][r]表示从l到r最少需要染几次色。
状态转移方程:
1.f[l][r]=min(f[l][i],f[i+1][r]) (l<=i<r) 这段染色等于俩段分别染色,很好看出来。
2.if(s[l]==s[r]) f[l][r]=min(min(f[l+1][r],f[l][r+1]),f[l+1][r-1])。
分俩种一可以直接涂上俩端点,2是可以忽略掉左端点和右端点。
第2点作用是很关键的,它会解决类似ABACDA之类的问题。忽略掉一段端点之后就可以继续找俩个端点进行染色。
为什么是对的呢?因为最后无论如何都一个节点都需要涂一次,所有的同色的端点就也被涂了。
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn = 200 + 10; int n; int f[maxn][maxn]; char s[maxn]; int dp(int l,int r) { if(l>r) return 0; if(l==r) return 1; if(f[l][r]) return f[l][r]; int &res=f[l][r]; res=r-l+1; for(int i=l;i<r;i++) res=min(res,dp(l,i)+dp(i+1,r)); if(s[l]==s[r]) res=min(res,min(min(dp(l,r-1),dp(l+1,r)),dp(l+1,r-1)+1)); return res; } int main() { scanf("%s",s+1); printf("%d\n",dp(1,strlen(s+1))); return 0; }