压缩[SCOI2007]

题目描述  给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程

             

  另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。

输入

输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。

输出

输出仅一行,即压缩后字符串的最短长度。

样例输入

bcdcdcdcdxcdcdcdcd

样例输出

12



题解   把题目读懂就挺不容易的。。。考试的时候读了很久的题,很久没做过区间dp,没想到正解。大致写了一下每一位的值可以怎么得到,就被循环节给绕进去了。因为没有细致做这道题,样例也没有手动分析。下午改题之前动手模拟了一个测试点,就对“重复”这个定义的理解深了很多。
         f[i][j]表示i之前有一个重复开始,到j的最小长度。
         f[i][i]=2;在i之前放置起点
         bj(f[i][j],f[i][j-1]+1);确认之前更新的是否最优
         bj(f[i][j*2-i+1],f[i][j]+1);如果循环在继续,以i到j为一个循环节更新下一个循环节
         f[i][i]=1;起点本身没必要循环
         之后再类似弗洛伊德确认各区间最优值bj(f[i][j],f[i][k]+f[k+1][j]);
         结果即为f[0][n-1];
    关于dp还是要有更抽象的概念,如果纠结于细节就会越绕越深。要善于抓住问题的关键,列出状态,否则真是没办法做。对于和字符串有关的题一直觉得难做,其实也是因为不擅长把字符转化成数之间的关系。只要找准状态,严格按照状态设计程序,dp应该是有规律可循的。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
using namespace std;
int n,f[55][55];
string a;
bool d;
void bj(int &x,int y)
{
     x=x<y?x:y;
}
int main()
{
    freopen("compress.in","r",stdin);
    freopen("compress.out","w",stdout);
    memset(f,0x3f,sizeof(f));
    cin>>a;
    n=a.size();
    for(int i=0;i<n;i++)
    {
       f[i][i]=2;
       if(i==0)  f[i][i]=1;
       for(int j=i+1;j<n;j++)
       {
          bj(f[i][j],f[i][j-1]+1);
          if(j*2-i+1<n)
          {
            d=1;
            for(int k=0;k<=j-i;k++)
              if(a[i+k]!=a[j+k+1])
              {
                 d=0;
                 break;
              }
            if(d)
              bj(f[i][j*2-i+1],f[i][j]+1);
          }
       }
       f[i][i]=1;
    }
    for(int i=0;i<n;i++)
      for(int j=i;j<n;j++)
        for(int k=i;k<=j;k++)
          bj(f[i][j],f[i][k]+f[k+1][j]);
    printf("%d",f[0][n-1]);
    //while(1);
    return 0;
}

 

 
posted @ 2017-07-15 14:53  moyiii  阅读(240)  评论(0编辑  收藏  举报