luogu P2470 [SCOI2007]压缩

传送门

dalao们怎么状态都设的两维以上啊?qwq 完全可以一维状态的说

设\(f[i]\)为前缀i的答案,转移就枚举从前面哪里转移过来\(f[i]=min(f[j-1]+w(j,i))(j\in [1,i])\)

现在要知道\(w(i,j)\)怎么写,也就是区间\([i,j]\)的最小长度(要求区间最多只能在开头有一个W),首先不压缩的长度就是原长度,然后压缩的话先要在开头加W,然后每次压缩一个最长的可以拆成两个相同串的前缀,压缩完后长度会加上1(后面接R),减去那个前缀的一半长度,然后那个前缀会缩掉后一半.把这些所有的长度取min就是这个区间的答案.这个压缩过程可以结合样例理解

注意开头是默认加好了W的

细节详见代码

#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define uLL unsigned long long

using namespace std;
const int N=55;
il int rd()
{
    int x=0,w=1;char ch=0;
    while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    return x*w;
}
char cc[N];
uLL ha[N],bs[N];
il uLL hh(int l,int r){return ha[r]-ha[l-1]*bs[r-l+1];}
int n,f[N];
il int w(int l,int r)
{
    int an=r-l+1+(l==1),nw=1; //左端点为1,由于开头加好了W,所以这里不压缩长度先加上1平衡压缩要加上的W
    while(l<r)
    {
        int mid=(l+r)>>1;
        while(l<r&&hh(l,mid)!=hh(mid+1,r)) --r,++nw,mid=(l+r)>>1;
        if(l>=r) break;
        ++nw,r=mid;
        an=min(an,nw+r-l+1); //代价也可以看做M的个数+R的个数+后面的一些零碎字母+前半截前缀长度
    }
    return an;
}

int main()
{
    scanf("%s",cc+1),n=strlen(cc+1);
    bs[0]=1;
    for(int i=1;i<=n;++i) bs[i]=bs[i-1]*233,ha[i]=ha[i-1]*233+cc[i],f[i]=i+1;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=i;++j)
            f[i]=min(f[i],f[j-1]+w(j,i));
    printf("%d\n",f[n]-1);  //把开头没有的W减掉
}

原文地址:https://www.cnblogs.com/smyjr/p/10353647.html

时间: 2024-10-11 01:43:34

luogu P2470 [SCOI2007]压缩的相关文章

Luogu 2470 [SCOI2007]压缩

和Luogu 4302 [SCOI2003]字符串折叠 差不多的想法,区间dp 为了计算方便,我们可以假设区间[l, r]的前面放了一个M,设$f_{i, j, 0/1}$表示区间$[i, j]$中是否存在M 因为这题只能是二的幂次倍压缩,所以转移的时候枚举中点chk是否合法,如果合法那么 $f_{i, j, 0} = f_{i, (i + j) / 2 - 1, 0} + 1$ 除了区间压缩,还可以通过加法构成最优答案 1.当中间加入了M,枚举M加入的位置 $f_{i, j, 1} = min

bzoj 1068: [SCOI2007]压缩 DP

1068: [SCOI2007]压缩 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 496  Solved: 315[Submit][Status] Description 给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息.压缩后的字符串除了小写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没有M,则从串的开始算起)开始的解压结果(称为缓冲串). bcdcdcdcd可以

bzoj1068: [SCOI2007]压缩

区间dp. bool t代表区间内是否能含M. 如果不能含M的话有 res=min{f[l][i][0]+r-i}.(i<r) (串长的最小值等于前面串压缩后的最小值和不压缩后面串的长度). 如果字符串长度为偶数,且前半串等于后半串,还有 f[l][r][t]=min(f[l][(l+r)>>1][t]+1) (后半串用1个R替代). 如果t=1时,除上面俩个还有res=min{f[l][i][1]+1+f[i+1][r][1]}. 状态和3种状态转移方程比较难想.很大程度是因为对区间d

【BZOJ 1068】[SCOI2007]压缩

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

[BZOJ 1068] [SCOI2007] 压缩 【区间 DP 】

题目链接:BZOJ - 1068 题目分析 这种区间 DP 之前就做过类似的,也是字符串压缩问题,不过这道题稍微复杂一些. 需要注意如果某一段是 S1S1 重复,那么可以变成 M + Solve(S1) + R ,不过这个 Solve(S1) 中不能在中间有 M ,否则后面的 R 向前找到的 M 就不再是开头的 M 了. 代码 #include <iostream> #include <cstdio> #include <cstring> #include <al

【BZOJ】1068: [SCOI2007]压缩(dp)

http://www.lydsy.com/JudgeOnline/problem.php?id=1068 发现如果只设一维的话无法转移 那么我们开第二维,发现对于前i个来说,如果确定了M在哪里,第i个是用R还是不用就能确定了(如果用R那么在中间一定变成了缓冲串) 那么可以转移了 设d[i,j]表示前i个串,最近的一个M在i的前边一个格子,的最短长度,有 d[1,1]=1 d[i,i]=min{d[i-1,j]}+2 //即用一次M又补上i,所以+2 d[i,j]=d[pos,j]+1,其中pos

bzoj 1068: [SCOI2007]压缩【区间dp】

神区间dp 设f[l][r][0]为在l到r中压缩的第一个字符为M,并且区间内只有这一个M,f[l][r][0]为在l到r中压缩的第一个字符为M,并且区间内有两个及以上的M 然后显然的转移是f[i][j][1]=min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1,f[i][j][0]=f[i][j][0],f[i][k][0]+j-k 然后考虑合并串,也就是当(l,mid),(mid+1,r)的串相等的时候,转移f[i][j][

B1068 [SCOI2007]压缩 区间dp

这个题我状态想对了,但是转移错了...dp的代码难度都不大,但是思考含量太高了..不会啊,我太菜了. 其实这个题就是一个正常的区间dp,中间多了一个特判的转移就行了. 题干: Description 给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息.压缩后的字符串除了小 写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没 有M,则从串的开始算起)开始的解压结果(称为缓冲串). bcdcdcdcd可以压缩为bMcdRR

bzoj 1068: [SCOI2007]压缩

做之前可以先做一下这题 http://www.lydsy.com/JudgeOnline/problem.php?id=1090 本来是想做一道区间DP的 然而太弱 并没有很快理解如何用传统区间DP(区间合并)来写这题 于是先用自己yy的比较水的方法做了一遍(其实也就是模拟题意中的压缩操作) 用f[i][j]表示 现在原串处理好了第i位 且缓冲串长度为j时的最小花费 那么f[i][j]可以从这三种情况转移过来 f[i-1][j-1]+1 (填原字母) f[i-j/2][j/2]+1 (填R,j为