当前位置:网站首页>P6758 [BalticOI2013] Vim

P6758 [BalticOI2013] Vim

2022-06-21 19:59:00 doctorZ_

P6758 [BalticOI2013] Vim

题目描述

给定一个长度为 N N N 的字符串 S S S,Victor 的目标是将其中的 e 全部删除,而不删除其他字符。
Victor 使用 Vim 来解决这个问题。
然而,Victor 并不熟悉 Vim,他只知道这里面的三个指令:

  • x:删除光标处的字符,光标位置不变,不可以在最后的字符处使用这个命令。
  • h:将光标向左移动一格,如果光标位于第一个位置,则光标不动。
  • f:其后接一个字符 c c c,其将会将光标移至其右边的第一个字符 c c c c ≠ c\not = c=e
    请计算将其中的 e 全部删除,而不删除其他字符的最小按键数。

tips: \text{tips:} tips一次 f f f操作代价是 2 2 2,一开始看成 1 1 1得到了一些假结论,给 A A A组口胡时锅了

Solution

不难想到一个暴力 d p dp dp,设 f i , j f_{i,j} fi,j表示前 i i i个位置的 e e e都已经被删了,当前光标跳到了位置 j j j的最小代价,然后就可以做到 O ( n 2 v ) O(n^2v) O(n2v)的复杂度,其中 v v v为字符集大小
但是这不能接受,这时候就要引入一个叫做线头 d p dp dp的东西
我们首先把字符中的 e e e都给去掉,删掉一个 e e e有两步操作,从后面那个字符移过来,删掉它,所以删掉一个 e e e需要代价 2 2 2
那么剩下的字符之间我们看作有一些边,如果有两个相邻字符之间原本是有 e e e的,那这条边我们称之为
必经边,设 n d i nd_i ndi表示 i − 1 , i i-1,i i1,i这条边是否是必经边

图是从这里嫖的LOJ2687 BalticOI2013 Vim 线头DP
这是我们移动时的大致形态,观察可以得出,一条边要么被一条从后面过来的 f f f操作经过一次,要么被 f f f操作一次,然后 h h h回去,再 f f f过去经过 3 3 3次,必经边是必须被经过 3 3 3次的
根据这个我们就可以设计出 d p dp dp,设 f i , j f_{i,j} fi,j表示 1 ∼ i + 1 1\sim i+1 1i+1之间的边全部被经过,覆盖 i , i + 1 i,i+1 i,i+1一次的操作是 f j fj fj的最小代价, g i , j , k g_{i,j,k} gi,j,k表示先覆盖 i , i + 1 i,i+1 i,i+1用了 f j fj fj h h h回来,然后又用了 f k fk fk到了后面的最小代价
转移:
f i , j = min ⁡ { f i , j − 1   ( j ≠ s i & ! n d i )   ,   f i − 1 , s i + 2   ,   g i − 1 , s i , j   ( j ≠ s i )   ,   g i − 1 , s i , s i + 2   } f_{i,j}=\min\{f_{i,j-1}\ (j\not =s_i\& !nd_i)\ ,\ f_{i-1,s_i}+2\ ,\ g_{i-1,s_i,j}\ (j\not =s_i)\ ,\ g_{i-1,s_i,s_i}+2\ \} fi,j=min{ fi,j1 (j=si&!ndi) , fi1,si+2 , gi1,si,j (j=si) , gi1,si,si+2 }
g i , j , k = min ⁡ { f i − 1 , j + 3   ( j ≠ s i )   ,   f i − 1 , s i + 5   ,   g i − 1 , j , k + 1   ( j ≠ s i & k ≠ s i ) , g_{i,j,k}=\min\{f_{i-1,j}+3\ (j\not =s_i)\ ,\ f_{i-1,s_i}+5\ ,\ g_{i-1,j,k}+1\ (j\not =s_i\&k\not =s_i), gi,j,k=min{ fi1,j+3 (j=si) , fi1,si+5 , gi1,j,k+1 (j=si&k=si),
  g i − 1 , s i , k + 3   ( k ≠ s i )   , g i − 1 , j , s i + 3   ( j ≠ s i )   ,   g i − 1 , s i , s i + 5   } \ g_{i-1,s_i,k}+3\ (k\not = s_i)\ ,g_{i-1,j,s_i}+3\ (j\not =s_i)\ ,\ g_{i-1,s_i,s_i}+5\ \}  gi1,si,k+3 (k=si) ,gi1,j,si+3 (j=si) , gi1,si,si+5 }
然后就可以做到 O ( n v 2 ) O(nv^2) O(nv2)
一些细节:

  • 1. d p dp dp中的答案没有包括 e e e的代价最后要加上 2 2 2
  • 2.可以在字符串的最后加一个字符串中未出现的字符,删完 e e e以后我们视作直接跳到了最后一个字符,方便统计答案

code

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+1000,V=10;
int n,m,nume,p[N+10],nd[N+10],f[N+10][V+1],g[N+10][V+1][V+1];char s[N+10];
int main()
{
    
	scanf("%d %s",&n,s+1);
	for(int i=1;i<=n;i++)
	{
    
		if(s[i]!='e') p[++m]=s[i]-'a',nd[m]=(s[i-1]=='e');
		else nume++;
	}
	for(int i=1;i<=m;i++)
		for(int j=0;j<=V;j++)
		{
    
			for(int k=0;k<=V;k++) g[i][j][k]=3*n+100;
			f[i][j]=3*n+100;
		}
	for(int i=0;i<=V;i++) if(i!='e'-'a') f[1][i]=2;
	for(int i=2;i<=m;i++)
		for(int j=0;j<=V;j++)
		{
    
			if(j!=p[i]&&!nd[i]) f[i][j]=min(f[i][j],f[i-1][j]);
			f[i][j]=min(f[i][j],f[i-1][p[i]]+2);
			if(j!=p[i]) f[i][j]=min(f[i][j],g[i-1][p[i]][j]);
			f[i][j]=min(f[i][j],g[i-1][p[i]][p[i]]+2);
			for(int k=0;k<=V;k++)
			{
    
				if(j!=p[i]) g[i][j][k]=min(g[i][j][k],f[i-1][j]+3);
				g[i][j][k]=min(g[i][j][k],f[i-1][p[i]]+5);
				if(j!=p[i]&&k!=p[i]) g[i][j][k]=min(g[i][j][k],g[i-1][j][k]+1);
				if(j!=p[i]) g[i][j][k]=min(g[i][j][k],g[i-1][j][p[i]]+3);
				if(k!=p[i]) g[i][j][k]=min(g[i][j][k],g[i-1][p[i]][k]+3);
				g[i][j][k]=min(g[i][j][k],g[i-1][p[i]][p[i]]+5);
			}
		}
	printf("%d",f[m][10]+2*nume-2);
	return 0;
}
原网站

版权声明
本文为[doctorZ_]所创,转载请带上原文链接,感谢
https://blog.csdn.net/Z_Y_S_/article/details/125354580