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;
}

 

posted @ 2018-01-08 16:03  MSPqwq  阅读(156)  评论(0编辑  收藏  举报