【题解】CCPC-Final 2019 C - Mr. Panda and Typewriter

题目

题意

给一个打字机,有三种操作:

  1. 打一个字符花费X的时间
  2. 复制一段已经打过的子串到剪贴板,花费Y的时间
  3. 粘贴剪贴板内容到串末尾,剪贴板内容不变,花费Z的时间
    求打出给定串最短的用时
    要求复杂度 O ( n 2 ) O(n^2) O(n2)

题解

很容易想到DP,在观察一下这个状态是 n 2 n^2 n2的:
f [ i ] [ j ] : 表 示 已 经 输 入 了 i , 当 前 剪 贴 板 是 当 前 串 长 度 为 j 的 后 缀 , j = 0 表 示 剪 贴 板 为 空 f[i][j] : 表示已经输入了i,当前剪贴板是当前串长度为j的后缀,j=0表示剪贴板为空 f[i][j]:ijj=0。通过转移来说明为什么只需要记录剪贴板为后缀的情况:

  1. 重新剪贴再粘贴: f [ i ] [ j ] < − f [ i − j ] [ j ] + Y + Z f[i][j] <- f[i - j][j] + Y + Z f[i][j]<f[ij][j]+Y+Z
  2. 把上一段剪贴板中的东西再粘贴一次: f [ i ] [ j ] < − f [ p r e [ i ] [ j ] ] [ j ] + ( i − p r e [ i ] [ j ] − j ) ∗ X + Z f[i][j] <- f[pre[i][j]][j] + (i - pre[i][j] - j) * X + Z f[i][j]<f[pre[i][j]][j]+(ipre[i][j]j)X+Z 这里比较巧妙,因为剪贴板要再粘贴的话一定是在下一个出现位置,中间这一段都是单个输入。
  3. 若剪贴板为空,输入单个字符 : f [ i ] [ 0 ] < − f [ i − 1 ] [ 0 ] + X , 注 意 这 里 f [ i − 1 ] [ 0 ] 已 经 代 表 所 有 状 态 的 最 小 值 , 因 为 忽 视 剪 贴 板 内 容 不 需 要 代 价 f[i][0] <- f[i - 1][0] + X,注意这里f[i - 1][0]已经代表所有状态的最小值,因为忽视剪贴板内容不需要代价 f[i][0]<f[i1][0]+Xf[i1][0]
    可以看出,只要剪贴板中的内容还有用,那么我们一定会进行粘贴操作,此时剪贴板的内容就是新串的后缀。

然后是求: p r e [ i ] [ j ] : 表 示 在 i 结 尾 长 度 为 j 的 串 的 上 一 个 出 现 结 尾 位 置 pre[i][j]: 表示在i结尾长度为j的串的上一个出现结尾位置 pre[i][j]:ij。 这个用lcs来求最方便(不用hash,因为是对所有串进行处理)。利用更长串的信息,直接倒着扫一遍即可求出,详见代码。

#include
using namespace std;
#define PB push_back
#define lowbit(x) (x&(-x))
#define MP make_pair
#define fi first
#define se second
#define ls(x) (x << 1)
#define rs(x) ((x << 1) | 1)
#define rep(i,l,r) for (int i = l ; i <= r ; i++)
#define down(i,r,l) for (int i = r ; i >= l ; i--)
#define fore(i,x) for (int i = head[x] ; i ; i = e[i].next)
#define SZ(v) (int)v.size()

typedef long long ll;
typedef pair <int,int> pr;
const int maxn = 1e6 + 10;
const ll inf = 1e18;

void up (ll &x,ll y){
      if ( x > y ) x = y; }
ll solve(){
     
	int n; ll X,Y,Z;
	cin>>n>>X>>Y>>Z;
	vector <vector <int>> lcs(n,vector <int> (n)) , pre(n,vector <int> (n,-1));
	vector <int> s(n);
	rep(i,0,n - 1) cin>>s[i];
	rep(i,0,n - 1) rep(j,0,n - 1){
     
		if ( s[i] == s[j] ) lcs[i][j] = (i > 0 && j > 0) ? (lcs[i - 1][j - 1] + 1) : 1;
		else lcs[i][j] = 0;
	}
	rep(i,0,n - 1) rep(j,0,i - 1) pre[i][min(i - j,lcs[i][j])] = j;
	rep(i,0,n - 1) down(j,i - 1,0) pre[i][j] = max(pre[i][j],pre[i][j + 1]);
	vector <vector <ll>> f(n,vector <ll> (n,inf));
	f[0][0] = X;
	rep(i,1,n - 1){
     
		f[i][0] = f[i - 1][0] + X;
		rep(j,1,i){
     
			if ( pre[i][j] == -1 ) continue;
			up(f[i][j],f[pre[i][j]][j] + (i - pre[i][j] - j) * X + Z);
			up(f[i][j],f[i - j][0] + Y + Z);
			up(f[i][0],f[i][j]);
		}
	} 
	return f[n - 1][0];
}
int main(){
     
	ios_base::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	int cases;
	cin>>cases;
	rep(t,1,cases){
     
		cout<<"Case #"<<t<<": "<<solve()<<"\n";
	}
}

总结

也是复习,第二次做这个题目。还是觉得比较经典。这次还是以为要用hash来求pre,忘记了最简单的方法!

你可能感兴趣的:(DP,字符串)