BZOJ 1090[SCOI2003]字符串折叠 | 区间DP
题目:
http://www.lydsy.com/JudgeOnline/problem.php?id=1090
题解:
枚举长度,枚举左端点i,得到右端点j
两种更新:
1.枚举中间的点k,f[i][j]=min(~,f[i][k]+f[k+1][j])
2.i到j要压缩:枚举区间长度的约数,然后看合不合法,如果合法就更新就好了
#include<cstdio> #include<algorithm> #include<cstring> #define N 105 using namespace std; int f[N][N],n; char s[N]; void upt(int &x,int y) {if (x>y) x=y;} int calc(int x) { int ret=0; while (x>0) x/=10,ret++; return ret; } void check(int l,int r) { int len=r-l+1,ans=len; for (int i=1;i<=len;i++) if (len%i==0) { int ok=1; for (int j=l,k=l;j<=r;j++,k++) { if (k>l+i-1) k=l; if (s[j]!=s[k]) {ok=0;break;} } if (ok) upt(ans,f[l][l+i-1]+calc(len/i)+2); } upt(f[l][r],ans); } int main() { scanf("%s",s+1); n=strlen(s+1); for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) f[i][j]=j-i+1; for (int l=1;l<=n;l++) for (int i=1;i<=n;i++) { int j=i+l-1; if (j>n) break; for (int k=i;k<j;k++) upt(f[i][j],f[i][k]+f[k+1][j]); check(i,j); } printf("%d\n",f[1][n]); return 0; }