SCOI 2007 压缩 C++

来源:互联网 发布:刷单平台 php源码 编辑:程序博客网 时间:2024/06/12 21:37

                                  原题:Time Limit: 1 Sec  Memory Limit: 128 MB

Description

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

 

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

Input

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

Output

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


Sample Input 1

aaaaaaa

Sample Output 1

5

    Sample Input 2

bcdcdcdcdxcdcdcdcd

    Sample Output 2

12

HINT

在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。 

【限制】 

100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50










在此声明一下,我承认参考了其他人的相关文章,但也加入了自己的理解等。如有雷同请不要见怪。




首先讲讲解题思路吧:
设整个字符串长度为n。根据题意,我们可以看作在区间[1,n]之前已经放有一个字母M。那么我们就是要用一些M和R,将字符串中的重复串分割出来,并使最终压缩后的字符串尽量短,Begin都说了这是个区间DP了对吧。在例子bcdcdcdcdxcdcdcdcd中,cd我称之为重复串,而其中的cdcdcdcd便是题目所说的缓冲串。

个人认为呢,解题的关键便是重复串和缓冲串的寻找,由此将解题方向指向对M、R位置的寻找。所以我们仅仅需要用动态规划求出M的位置就离成功不远了。如果发现当前处理的字符串中不含M,并且左右两半是一样的,那么就在中点处放一个R并去掉后面那一半就行了。比如:cdcd>>cdRcd>>cdR
然后我们就可以定义数组f[left][right][ok],用f来表示输入中left到right的部分字符串,如果ok=1则表示该部分字符串中有放M,若ok=0则反之。


现在来考虑考虑边界情况:
首先来#define INF 0x3f3f3f3f 弄个大数字吧(别问我为什么hhhhh)
边界条件是f[][][0]=1(只有一个字符不能放M呢,不然就更大了。你看b还要压缩吗),f[][][1]=INF
这边界条件怎么出来的应该一个个自己想一会都想得出吧......
我懒就懒得解释了......再说了你们那么聪明肯定会懂的对吧!

那么现在对于left到right的部分开始做处理:
如果该部分内放了M,那么我们就暴力枚举出其中一个M放在哪里,并用分割后的两个部分的状态做转移。
若该部分内没放M,我们先判断这个区间是否是一个缓冲串(左右两半的字符串一样),
如果是的话那么这个部分只需要左半部分压缩后的长度再加上一个R就够了。
不然我们就枚举R放在哪里,这样这个R就和前面最近的那个M对应起来,分割出来了一个重复串部分,
然后再用这个重复串部分的函数值做转移,R后面保持不变。

最后print出一个最小值就OK了。


程序自己看咯,建议对照着上面的文字看,更容易看得懂。

程序:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#define maxn 55
#define INF 0x3f3f3f3f
using namespace std;
int f[maxn][maxn][2],n;
char st[maxn];
bool bo(int left,int right) 
{
    if ((right-left+1)&1) 
        return false; 
    int m=(left+right)>>1;
    for (int i=left;i<=m;i++)
        {
            int j=m+i-left+1;
            if(st[i]!=st[j])
                return false;
        }
    return true;
}
  
int dp(int left,int right,bool ok)
{
    if (f[left][right][ok]<INF) 
        return f[left][right][ok];
    int len=right-left+1; 
    if (len==1)
        {
            if (ok) 
                return f[left][right][ok]=INF; 
            else
                return f[left][right][ok]=1;
        }
    int m=(left+right)>>1;
    if (ok)
        {
            for (int i=left;i<right;i++)
                {
                    f[left][right][ok]=min(f[left][right][ok],dp(left,i,1)+1+dp(i+1,right,1));
                    f[left][right][ok]=min(f[left][right][ok],dp(left,i,0)+1+dp(i+1,right,1));
                    f[left][right][ok]=min(f[left][right][ok],dp(left,i,1)+1+dp(i+1,right,0));
                    f[left][right][ok]=min(f[left][right][ok],dp(left,i,0)+1+dp(i+1,right,0));
                }
            return f[left][right][ok];
        }
    if (bo(left,right))
        f[left][right][ok]=dp(left,m,0)+1;
    for (int i=left;i<right;i++)
        f[left][right][ok]=min(f[left][right][ok],dp(left,i,0)+right-i);
    return f[left][right][ok];
}
  
int main()
{
    memset(f,INF,sizeof(f));
    scanf("%st",st+1);
    n=strlen(st+1);
    printf("%d\n",min(dp(1,n,0),dp(1,n,1)));
    return 0;
}


原创粉丝点击