题意
分析
大佬题解
因为删掉每个
e
e
e前必然是先走到
e
e
e后面的一个字母,然后花费
2
2
2的代价把
e
e
e删掉,所以我们可以先把
e
e
e去掉,转化成有一些位置必须要经过,最后再把答案加上
e
的
个
数
∗
2
e的个数*2
e的个数∗2就好了。
注意到一段区间
[
i
,
i
+
1
]
[i,i+1]
[i,i+1]要么是被一步跨过,要么是先被一步跨过,再走回去,然后一步跨过。
设
f
i
,
j
f_{i,j}
fi,j表示区间
[
i
,
i
+
1
]
[i,i+1]
[i,i+1]是被一步跨过,且这一步走的是字符
j
j
j的最小代价。
g
i
,
j
,
k
g_{i,j,k}
gi,j,k表示区间
[
i
,
i
+
1
]
[i,i+1]
[i,i+1]被跨过两次,且第一次走的是字符
j
j
j,第二次走的是字符
k
k
k的最小代价。
在转移的时候讨论区间
[
i
−
1
,
i
]
[i-1,i]
[i−1,i]是被跨过一次还是跨过两次,第一次是否被同一步跨过,第二次是否被同一步跨过即可。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
const int N=70005;
const int inf=1000000000;
int n,f[N][15],g[N][15][15],s[N];
bool vis[N];
char str[N];
int main()
{
scanf("%d",&n);
scanf("%s",str+1);
int m=0,cnt=0;bool flag=1;
for (int i=1;i<=n;i++)
if (str[i]=='e') cnt++,flag=1;
else s[++m]=str[i]-'a',vis[m]=flag,flag=0;
n=m;
for (int i=0;i<=10;i++)
{
f[0][i]=inf;
for (int j=0;j<=10;j++) g[0][i][j]=inf;
}
f[0][s[1]]=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=10;j++)
{
int w=inf;
if (!vis[i]&&j!=s[i]) w=std::min(w,f[i-1][j]);
w=std::min(w,f[i-1][s[i]]+2);
if (j!=s[i]) w=std::min(w,g[i-1][s[i]][j]);
w=std::min(w,g[i-1][s[i]][s[i]]+2);
f[i][j]=w;
for (int k=0;k<=10;k++)
{
w=inf;
if (j!=s[i]) w=std::min(w,f[i-1][j]+3);
w=std::min(w,f[i-1][s[i]]+5);
if (j!=s[i]&&k!=s[i]) w=std::min(w,g[i-1][j][k]+1);
if (k!=s[i]) w=std::min(w,g[i-1][s[i]][k]+3);
if (j!=s[i]) w=std::min(w,g[i-1][j][s[i]]+3);
w=std::min(w,g[i-1][s[i]][s[i]]+5);
g[i][j][k]=w;
}
}
printf("%d\n",f[n][10]-2+cnt*2);
return 0;
}