题目传送门:
https://ac.nowcoder.com/acm/problem/21337
题目大意:
通过以下三种操作,将字符串S变成回文串。如果不能实现,输出-1,否者输出最小的花费
1:在任意位置增加一个字符
2:删除一个字符
3:改变一个字符
输入:
一个字符串S、和整数M
用M条语句来描述能进行的操作
add c x 表示增加c字符需要x的代价
erase c x表示删除c字符需要x的代价
change c1 c2 x表示将c1 改成c2需要x的代价
思路:对于回文串来说,我们要各个元素匹配,将当前元素删除和在串的另外一端添加当前元素是等价的。而对于匹配而言,如果某个元素已经匹配了,则相当于将他删除,不再在子问题中考虑。
状态转移的时候,我们有三种情况讨论:
1、将左端点元素str[l] 匹配掉
2、将右端点元素str[r] 匹配掉
3、同时将str[l]和str[r] 匹配掉
所以很快我们可以得出状态转移:
for(int len=1;len<=lenth;len++){
for(int l=0;l+len-1<lenth;l++){
int r=l+len-1;
f[l][r]=INF;
if(str[l]==str[r]) f[l][r]=min(f[l][r],f[l+1][r-1]);
f[l][r]=min(f[l][r],f[l+1][r]+cost[str[l]-'a']);
f[l][r]=min(f[l][r],f[l][r-1]+cost[str[r]-'a']);
for(int k=0;k<26;k++){
LL c1=g[str[l]-'a'][k], c2=g[str[r]-'a'][k];
f[l][r]=min(f[l][r],f[l+1][r-1]+c1+c2);
}
}
}
这个写法是区间dp的一种较为可读的写法。另外补充cost i 是小写字母i匹配掉的最小花费,g(i,j) 是将小写字母i变成小写字母j的最小花费。通过这种状态转移,我们不遗漏的考虑了所有匹配的情况,接下来我们要实现求costi 和 g(i,j) 的函数
void floyd(){
for(int i=0;i<26;i++) g[i][i]=0;
for(int k=0;k<26;k++)
for(int i=0;i<26;i++)
for(int j=0;j<26;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
for(int i=0;i<26;i++){
cost[i]=min(cost[i],min(costa[i],coste[i]));
for(int j=0;j<26;j++){
cost[i]=min(cost[i],g[i][j]+min(costa[j],coste[j]));
cost[i]=min(cost[i],costa[j]+g[j][i]);
for(int k=0;k<26;k++){
cost[i]=min(cost[i],g[i][j]+costa[k]+g[k][j]);
}
}
}
}
所有的小写字母会映射到一个整数值,我们用 str-'a’来映射。当然这不是重点。我们利用弗洛伊德算法求出了字符i转化到字符j的最小代价,另外我们还维护了一个很重要的东西cost 数组:
我来介绍下他的作用,cost i 表示将字符i 匹配掉的最小花费,有四种情况:
1、将i删除,或者在另一端添加i
2、将i变成j,将j删除或者在另一端添加j
3、i不变,另一端添加j,将j变成i
4、将i变成k,另一端添加j,将j变成k
四种情况不重不漏将cost i的最小值讨论出来
下面贴下总的代码:(感觉比acwing的动态规划题更难QWQ,不过y总真算把我教会了)
注意操作可能比较多,花费比较大,最好开LL来存,避免不必要的错误
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=55,INF=1e14;
LL g[N][N];
LL cost[N],costa[N],coste[N];
LL f[N][N];
void init(){
for(int i=0;i<=26;i++){
costa[i]=coste[i]=cost[i]=INF;
for(int j=0;j<=26;j++){
if(i==j) g[i][j]=0;
g[i][j]=INF;
}
}
}
void floyd(){
for(int i=0;i<26;i++) g[i][i]=0;
for(int k=0;k<26;k++)
for(int i=0;i<26;i++)
for(int j=0;j<26;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
for(int i=0;i<26;i++){
cost[i]=min(cost[i],min(costa[i],coste[i]));
for(int j=0;j<26;j++){
cost[i]=min(cost[i],g[i][j]+min(costa[j],coste[j]));
cost[i]=min(cost[i],costa[j]+g[j][i]);
for(int k=0;k<26;k++){
cost[i]=min(cost[i],g[i][j]+costa[k]+g[k][j]);
}
}
}
}
int main(){
string str;
cin>>str;
int lenth=str.size();
int m;
cin>>m;
init();
for(int i=0;i<m;i++){
string t1;
cin>>t1;
if(t1[0]=='c'){
char d,e;
LL c;
cin>>d>>e>>c;
g[d-'a'][e-'a']=min(g[d-'a'][e-'a'],c);
}
else if(t1[0]=='a'){
char d;
LL c;
cin>>d>>c;
costa[d-'a']=min(costa[d-'a'],c);
}
else{
char d;
LL c;
cin>>d>>c;
coste[d-'a']=min(coste[d-'a'],c);
}
}
floyd();
memset(f,0,sizeof f);
for(int len=1;len<=lenth;len++){
for(int l=0;l+len-1<lenth;l++){
int r=l+len-1;
f[l][r]=INF;
if(str[l]==str[r]) f[l][r]=min(f[l][r],f[l+1][r-1]);
f[l][r]=min(f[l][r],f[l+1][r]+cost[str[l]-'a']);
f[l][r]=min(f[l][r],f[l][r-1]+cost[str[r]-'a']);
for(int k=0;k<26;k++){
LL c1=g[str[l]-'a'][k], c2=g[str[r]-'a'][k];
f[l][r]=min(f[l][r],f[l+1][r-1]+c1+c2);
}
}
}
if(f[0][lenth-1]==INF) puts("-1");
else cout<<f[0][lenth-1]<<endl;
return 0;
}
```2022年4月13日,暴雨————————————于南昌