P6758 [BalticOI2013] Vim 题解
SDNetFriend · · 题解
转化
首先可以看出,那些 e 的位置对答案的贡献是固定的。当我们走到一段连续的 e 的前方,便可以后退一次删一次,如此反复,每个 e 的贡献就固定是 e 从原序列剔除,并且我们把每一个左边有 e 的位置都标记为关键点,因为这些点必须经过才能删除 e。
于是问题变为了:可以花费
简化
根据官方题解或者感性理解或者经过严谨证明,可以得知最终的路线一定是向右跳若干步,然后向左走一段,然后继续向右跳。并且任何两段向左走的区间都不可能有交,否则一定不优。于是就像很多题解所写的那样,路径一定长成这样:(从博客园 cjoier_Itst 大佬处借了个图)
这就变成了一个“序列上的复杂路径”,考虑用线头 DP 解决。
动态规划
我们给路径切片,第
最终我们的路径是由一片片这样的路径片拼起来形成的,我们实际上就是在决策第
我们考虑设状态分别为
分别表示:考虑完前
我们叫第一次跳跃为上方跳跃,第二次为下方跳跃(即两条绿色的曲线)。
注意这个绿色曲线不一定只跳了一次,可能是由很多次跳跃组成的。
那我们又怎么计算代价呢?考虑有几种边界情况:
(只画出了一部分情况)。
就是说,对向右跳这个过程,存在“起跳”和“降落”的位置,而我们考虑只在起跳的位置把贡献加上去就可以了。
通俗一点讲,我们以第一张图为例,假设现在的情况是:
左边是第
当且仅当第
以此类推,这个题就可以 DP 了,转移方程:(
以上是对
- 没降落也没起跳,只被覆盖一次,因为
i 不会被经过所以不能是关键点 。 - 在当前位置降落了,并继续向
x 去跳。 - 第
i-1 片被覆盖了三次,并且其对应的上方(就是设计状态那个上)跳跃的终点位置在i 。 - 第
i-1 片被覆盖了三次,并且其对应的上方下方的跳跃终点位置都在i ,此时需要从i 处接着下方跳跃起跳。
以上是对
- 上下两个跳跃都没降落也没起跳,向左走的那一步产生了
1 的贡献,接下来的每一个状态都有向左走的1 的贡献。 - 下方跳跃在点
i 降落并重新起跳,额外产生2 的贡献。 - 上方跳跃在点
i 降落并重新起跳,额外产生2 的贡献。 - 上下方跳跃都在
i 降落并重新起跳,额外产生4 的贡献。 - 第
i-1 片被覆盖了一次,并且其在i 处降落,所以上方从i 起跳产生了2 的贡献,因为左侧只被覆盖一次,所以下方跳跃也从i 起跳,再次产生2 的贡献。 - 第
i-1 片只被覆盖了一次,并且上方跳跃没有降落。此时i 会作为下方跳跃的起点,产生2 的贡献。
知道这些就可以愉快地 DP 了。初始我们设 k,使最后一次起跳终点为 k,那我们这部分的答案就是
代码
转移顺序和上述一致。
#include <bits/stdc++.h>
using namespace std;
inline int read(){
char c;int f=1,res=0;
while(c=getchar(),!isdigit(c))if(c=='-')f*=-1;
while(isdigit(c))res=res*10+c-'0',c=getchar();
return res*f;
}
const int N=7e4+5,A=10;
int n,a[N],ans0,ans;
char s[N];bool flg[N];
inline void init(){
int lst=0,_n=0;
for(int i=1;i<=n;++i){
if(s[i]!='e'){
a[++_n]=(s[i]>'e'?s[i]-'a'-1:s[i]-'a');
if(lst)flg[_n]=true,lst=0;
}else ans0+=2,lst=i;
}n=_n;
}int f[2][A],g[2][A][A];
inline void umin(int &x,int y)
{if(y<x)x=y;}
inline void solve(){
memset(f,0x3f,sizeof f);
memset(g,0x3f,sizeof g);
f[0][a[1]]=0;
for(int i=1;i<=n;++i){
swap(f[0],f[1]);swap(g[0],g[1]);
memset(f[0],0x3f,sizeof f[0]);
memset(g[0],0x3f,sizeof g[0]);
int c=a[i];
for(int x=0;x<A;++x){
if(!flg[i]&&x!=c)umin(f[0][x],f[1][x]);
umin(f[0][x],f[1][c]+2);
if(x!=c)umin(f[0][x],g[1][c][x]);
umin(f[0][x],g[1][c][c]+2);
}
for(int x=0;x<A;++x)
for(int y=0;y<A;++y){
if(x!=c&&y!=c)umin(g[0][x][y],g[1][x][y]+1);
if(y!=c)umin(g[0][x][y],g[1][c][y]+3);
if(x!=c)umin(g[0][x][y],g[1][x][c]+3);
umin(g[0][x][y],g[1][c][c]+5);
umin(g[0][x][y],f[1][c]+5);
if(x!=c)umin(g[0][x][y],f[1][x]+3);
}
}ans=f[0][A-1]-2;
}
int main(){
freopen("vim.in","r",stdin);
freopen("vim.out","w",stdout);
n=read();scanf("%s",s+1);
init();solve();
printf("%d",ans0+ans);
return 0;
}