卧薪尝胆,厚积薄发。
SCOI2007 压缩
Date: Sat Sep 15 14:56:16 CST 2018
In Category:
NoCategory
Description:
给字符串压缩,可以用
$M$
标记串的开始,用
$R$
重复到上一个
$M$
的解压结果,问一个字符串最少能压缩到多长。
$1\le n\le 50$
Solution:
一个比较好想的区间
$DP$
思路是用
$f[l][r]$
表示从
$l$
到
$r$
的字符串压缩的最小长度,转移为
$f[l][r]=\min(f[l][k],f[k+1][r])$
和
$l[l][r]=\min(f[l][r],f[l][\frac{l+r}2]+2)(s[l,mid]=s[mid+1,r])$
。
但是这样在
$l$
~
$\frac{l+r}2$
中有
$M$
时最后的
$R$
并不会将
$l$
~
$\frac{l+r}2$
重复,而是会将中间的
$M$
到最后重复,要解决这个问题也很简单,只要强行保证
$l$
~
$\frac{l+r}2$
中没有
$M$
即可,于是设
$f[i][j][0/1]$
表示从
$l$
到
$r$
中间能否有
$M$
,注意这个状态代表在这之前强行补上一个
$M$
,因为如果没有
$M$
的话
$R$
直接从开头开始,转移要么是分成两段,即
$(M)\times\times R\times\times$
,由于状态的定义,只有在第三维为
$1$
时才能转移,要么是这一段可以分解成两段一样的,转移为
$f[l][r][0/1]=f[l][\frac{l+r}2]+1$
,然而还有第三种转移,就是如果后面直接复制没有任何操作的话,中间那个
$M$
就不用加了。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXL 51
char c[MAXL];
int f[MAXL][MAXL][2];
bool equ(int l,int r)
{
int len = (r - l + 1);
if(len % 2 == 1)return false;
len /= 2;
for(int i = l;i <= l + len - 1;++i)
{
if(c[i] != c[i + len])return false;
}
return true;
}
int dp(int l,int r,int t)
{
if(l == r)return 1;
if(f[l][r][t] != -1)return f[l][r][t];
int ans = (r - l + 1);
if(t)for(int i = l;i < r;++i)ans = min(ans,dp(l,i,t) + dp(i + 1,r,t) + 1);
for(int i = l;i < r;++i)ans = min(ans,dp(l,i,t) + r - i);
if(equ(l,r))ans = min(ans,dp(l,(l + r) >> 1,0) + 1);
return (f[l][r][t] = ans);
}
int main()
{
scanf("%s",c + 1);
memset(f,-1,sizeof(f));
int l = strlen(c + 1);
cout << dp(1,l,1) << endl;
return 0;
}
In tag:
DP-区间DP
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡