LOJ 2687 或 JZOJ 3320. 「BalticOI 2013」Vim

H y p e r l i n k Hyperlink Hyperlink

https://loj.ac/problem/2687
LOJ 2687 或 JZOJ 3320. 「BalticOI 2013」Vim_第1张图片


D e s c r i p t i o n Description Description

给定一个长度为 n n n的串,有三种操作

  1. 花费1点代价,删除光标处的字符
  2. 花费1点代价,左移光标
  3. 花费两点代价,位移到下一个光标字符处

求删除所有的字符 e e e的最小代价

数据范围: n ≤ 7 × 1 0 4 , 字 符 集 大 小 A ≤ 10 n\leq 7\times 10^4,字符集大小A\leq 10 n7×104,A10


S o l u t i o n Solution Solution

大佬题解链接

线头 d p dp dp

容易发现删除操作只会进行 n u m e num_e nume次,而且我们不可能会进行跳转到下一个 e e e的操作,因为如果这样我们还要掉头回来删掉原来那个 e e e

因此,可以发现每个 e e e右边的第一个非 e e e字符是必经字符,考虑把所有 e e e删掉,只在必经字符上进行 d p dp dp

f [ i ] [ j ] f[i][j] f[i][j]表示经过 [ i , i + 1 ] [i,i+1] [i,i+1]这条线段1次,跳到 j j j这个字符的最少跳跃代价
g [ i ] [ j ] [ k ] g[i][j][k] g[i][j][k]表示经过 [ i , i + 1 ] [i,i+1] [i,i+1]这条线段3次,左移前位置为 j j j,跳到 k k k这个字符的最小跳跃代价

说白了就是 f f f表示跳到 j j j g g g表示先跳到 j j j,折返过来,再跳到 k k k

转移方程:

  1. j ≠ s i   a n d   n e e d [ i ] = = 0 j\neq s_i\ and\ need[i]==0 j̸=si and need[i]==0,则我们可以直接从上一个 j j j直接继承过来, f [ i ] [ j ] = f [ i − 1 ] [ j ] f[i][j]=f[i-1] [j] f[i][j]=f[i1][j]
  2. 我们可以从 i i i这个位置跳转而来, f [ i ] [ j ] = f [ i − 1 ] [ s [ i ] ] + 2 f[i][j]=f[i-1][s[i]]+2 f[i][j]=f[i1][s[i]]+2
  3. j ≠ s i j\neq s_i j̸=si时 我们可以直接继承上次折返后的结果, f [ i ] [ j ] = g [ i − 1 ] [ s [ i ] ] [ j ] f[i][j]=g[i-1][s[i]][j] f[i][j]=g[i1][s[i]][j]
  4. 也可以从上次折返跳过来, f [ i ] [ j ] = g [ i − 1 ] [ s [ i ] ] [ s [ i ] ] + 2 f[i][j]=g[i-1][s[i]][s[i]]+2 f[i][j]=g[i1][s[i]][s[i]]+2

对于 g g g

  1. j ≠ s i j\neq s_i j̸=si时,从上一个 f f f继承过来,注意这里继承来之后变成 g g g要折返且要跳跃,所以 g [ i ] [ j ] [ k ] = f [ i − 1 ] [ j ] + 3 g[i][j][k]=f[i-1][j]+3 g[i][j][k]=f[i1][j]+3
  2. 同样我们可以从上一个 f f f跳过来, g [ i ] [ j ] [ k ] = f [ i − 1 ] [ s [ i ] ] + 2 g[i][j][k]=f[i-1][s[i]]+2 g[i][j][k]=f[i1][s[i]]+2
  3. j ≠ s i   a n d   k ≠ s i j\neq s_i\ and\ k\neq s_i j̸=si and k̸=si时,我们还可以从上一个 g g g折返过来, g [ i ] [ j ] [ k ] = g [ i − 1 ] [ j ] [ k ] + 1 g[i][j][k]=g[i-1][j][k]+1 g[i][j][k]=g[i1][j][k]+1
  4. k ≠ s i k\neq s_i k̸=si时,先折返再在 j j j跳, g [ i ] [ j ] [ k ] = g [ i − 1 ] [ s [ i ] ] [ k ] + 3 g[i][j][k]=g[i-1][s[i]][k]+3 g[i][j][k]=g[i1][s[i]][k]+3
  5. j ≠ s i j\neq s_i j̸=si时,先折返再跳到 s i s_i si g [ i ] [ j ] [ k ] = g [ i − 1 ] [ j ] [ s [ i ] ] + 3 g[i][j][k]=g[i-1][j][s[i]]+3 g[i][j][k]=g[i1][j][s[i]]+3
  6. 最后我们也可以疯狂跳跃(二连跳), g [ i ] [ j ] [ k ] = g [ i − 1 ] [ s [ i ] ] [ s [ i ] ] + 5 g[i][j][k]=g[i-1][s[i]][s[i]]+5 g[i][j][k]=g[i1][s[i]][s[i]]+5

最后,我们知道 f f f g g g只和上一项相关,所以可以滚动,滚一滚一不小心内存榜第一了QwQ
LOJ 2687 或 JZOJ 3320. 「BalticOI 2013」Vim_第2张图片
时间复杂度: O ( n A 2 ) O(nA^2) O(nA2)
空间复杂度: O ( n A 2 ) O(nA^2) O(nA2)滚动后 O ( m i n ( n , A 2 ) ) O(min(n,A^2)) O(min(n,A2)),当然你也可以边输入边搞,就优化到 O ( A 2 ) O(A^2) O(A2),这基本就是一个常数了QwQ


C o d e Code Code

#include
#include
#include
#define N 70010
#define A 11
using namespace std;int n,zd,gs,m;
char c;
int f[2][A],g[2][A][A],s[N];
bool need[N],p,o=1;
inline int read()
{
   int d=1,f=0;char c;
   while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
   while(c=getchar(),c>47&&c<58) f=(f<<3)+(f<<1)+c-48;
   return d*f;
}
signed main()
{
   n=read();
   for(register int i=1;i<=n;++i)
   {
   	while(c=getchar(),!islower(c));
   	if(c=='e') zd=1,gs++;
   	else {need[++m]=zd;s[m]=c-'a';zd=0;}
   }
   for(register int i=0;i<A;++i)
   {
   	for(register int j=0;j<A;++j) g[0][i][j]=0x3f3f3f3f;
   	f[0][i]=0x3f3f3f3f;
   }
   f[0][s[1]]=0;
   for(register int i=1;i<=m;p^=1,o^=1,++i)
    for(register int j=0;j<A;++j)
   {
   	int t=0x3f3f3f3f;
   	if(j!=s[i]&&!need[i]) t=min(t,f[p][j]);
   	t=min(t,f[p][s[i]]+2);
   	if(j!=s[i]) t=min(t,g[p][s[i]][j]);
   	t=min(t,g[p][s[i]][s[i]]+2);
   	f[o][j]=t;
   	for(register int k=0;k<A;k++)
   	{
   		t=0x3f3f3f3f;
   		if(j!=s[i]) t=min(t,f[p][j]+3);
   		t=min(t,f[p][s[i]]+5);
   		if(j!=s[i]&&k!=s[i]) t=min(t,g[p][j][k]+1);
   		if(k!=s[i]) t=min(t,g[p][s[i]][k]+3);
   		if(j!=s[i]) t=min(t,g[p][j][s[i]]+3);
   		t=min(t,g[p][s[i]][s[i]]+5);
   		g[o][j][k]=t;
   	}
   }
   printf("%d",f[p][10]+2*gs-2);
}

你可能感兴趣的:(线头dp)