【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=(i+j-1)/2,且是整数(就是确定了M找前一个R),且s[j, pos]=s[pos+1, i]

d[i,j]=min{d[i,j], d[k, j]+i-k}

答案就是min{d[n,i], 1<=i<=n}

因为数据小所以直接暴力处理,也就是n^3的,n^2的话应该挺好优化的

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
#define mkpii make_pair<int, int>
#define pdi pair<double, int>
#define mkpdi make_pair<double, int>
#define pli pair<ll, int>
#define mkpli make_pair<ll, int>
#define rep(i, n) for(int i=0; i<(n); ++i)
#define for1(i,a,n) for(int i=(a);i<=(n);++i)
#define for2(i,a,n) for(int i=(a);i<(n);++i)
#define for3(i,a,n) for(int i=(a);i>=(n);--i)
#define for4(i,a,n) for(int i=(a);i>(n);--i)
#define CC(i,a) memset(i,a,sizeof(i))
#define read(a) a=getint()
#define print(a) printf("%d", a)
#define dbg(x) cout << (#x) << " = " << (x) << endl
#define error(x) (!(x)?puts("error"):0)
#define printarr2(a, b, c) for1(_, 1, b) { for1(__, 1, c) cout << a[_][__]; cout << endl; }
#define printarr1(a, b) for1(_, 1, b) cout << a[_] << ‘\t‘; cout << endl
inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<‘0‘||c>‘9‘; c=getchar()) if(c==‘-‘) k=-1; for(; c>=‘0‘&&c<=‘9‘; c=getchar()) r=r*10+c-‘0‘; return k*r; }
inline const int max(const int &a, const int &b) { return a>b?a:b; }
inline const int min(const int &a, const int &b) { return a<b?a:b; }

const int N=52, oo=0x3f3f3f3f;
int d[N][N], n;
char s[N];
bool check(int i, int j) {
	int pos=(i+j-1), k=1;
	if(pos&1) return 0;
	pos>>=1;
	if(d[pos][j]>=oo) return 0;
	for1(x, j, pos) { if(s[x]!=s[pos+k]) return 0; ++k; }
	return 1;
}
int main() {
	scanf("%s", s+1);
	n=strlen(s+1);
	CC(d, oo);
	d[1][1]=1;
	for1(i, 2, n) {
		for1(j, 1, i-1) d[i][i]=min(d[i-1][j], d[i][i]);
		d[i][i]+=2;
		for1(j, 1, i-1) {
			if(check(i, j)) d[i][j]=d[(i+j-1)>>1][j]+1;
			for1(k, j, i-1) d[i][j]=min(d[i][j], d[k][j]+i-k);
		}
	}
	int ans=oo;
	for1(i, 1, n) ans=min(d[n][i], ans);
	print(ans);
	return 0;
}

  


Description

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

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

Input

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

Output

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

Sample Input

bcdcdcdcdxcdcdcdcd

Sample Output

12

HINT

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

【限制】

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

Source

时间: 2024-10-08 07:30:03

【BZOJ】1068: [SCOI2007]压缩(dp)的相关文章

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可以

[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】

神区间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][

BZOJ 1068 [SCOI2007]压缩 区间DP

题意:链接 方法:区间DP 解析: MD写题解(吐槽)写到一半markdown挂了什么鬼! 要不要这样!你知道我的内心是什么样的吗! 吐槽,啊呸,写题解写到一半突然丢失了我的内心是崩溃的好吗! 来我们重新写题解(吐槽) 这道题我刚开始列了个瞎(和谐)动规(二维的裸区间) 加上乱七八糟的判断是否有M后,居然有交叉! 一定是我逻辑错误,对就是这样! 后来又是一顿瞎(和谐)搞之后,代码抽的爆炸,然后我一测,c-free挂掉- - 过了一个小时后,我选择死亡. 然后看了一眼hzw的题解. 看到那个三维之

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为

BZOJ 1087状态压缩DP

状态压缩DP真心不会写,参考了别人的写法. 先预处理出合理状态, 我们用二进制表示可以放棋子的状态,DP[I][J][K]:表示现在处理到第I行,J:表示第I行的状态,K表示现在为止一共放的棋子数量. #include<stdio.h> #include<iostream> #define N 1111 using namespace std; typedef long long ll; int num,n,m; ll dp[11][1<<11][90]; int hh

BZOJ 2734 集合选数(状态压缩DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2734 题意:给出一个由1到n的数字组成的集合.定义合法子集为若x在子集中则2x.3x均不能在子集中.求有多少个合法的子集. 思路: 1   3    9 2   6    12 4   12   36 对于上面的矩阵,我们发现就等价于不选相邻数字的方案数.因此枚举每个还没有用到的数字,建立以该数字为左上角的矩阵.接着就是状态压缩DP. int a[N][N]; i64 f[2][1<<

BZOJ 1068

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1068 题意:字符串压缩.M表示一个重复串的开始,R表示与其前面一个M之间的重复.压缩出最短的串. 思路:f[i][j][0]还没有M,f[i][j][1]已经有M. char s[N]; int ok(int L,int R) { int M=(R-L+1)>>1; int i; for(i=0;i<M;i++) if(s[L+i]!=s[L+M+i]) return 0; r

POJ 3254 Corn Fields 状态压缩DP (C++/Java)

http://poj.org/problem?id=3254 题目大意: 一个农民有n行m列的地方,每个格子用1代表可以种草地,而0不可以.放牛只能在有草地的,但是相邻的草地不能同时放牛, 问总共有多少种方法. 思路: 状态压缩的DP. 可以用二进制数字来表示放牧情况并判断该状态是否满足条件. 这题的限制条件有两个: 1.草地限制. 2.相邻限制. 对于草地限制,因为输入的时候1是可以种草地的. 以"11110"草地分析,就只有最后一个是不可以种草的.取反后得00001  .(为啥取反