牛牛的回文串//区间DP(带有稍微简单最短路预处理)
我认为难点还是在于细节吧,想明白还是很有意思的一道经典DP。
题意:
牛牛喜欢回文串,牛妹给了牛牛一个字符串S,牛牛想把S变成回文串
牛牛可以做如下三种操作
1:在任意位置增加一个字符
2:删除一个字符
3:改变一个字符每种操作都有限定的字符,比如,只能删除’a’,增加’b’,把’c’变成’d’等等
每种操作都有相应的代价
用M条语句来描述能进行的操作
add c x 表示增加c字符需要x的代价
erase c x表示删除c字符需要x的代价
change c1 c2 x表示将c1 改成c2需要x的代价
求牛牛想要得到回文串需要的最少代价
如果不行输出-1
首先看到这个问题,我们应该很容易确定这是一道区间DP题,因为这是从小区间到大区间动态规划的过程。
以我现在DP能力来讲,最困难的事情便是for循环的运作,而我水完这道题有个很轻松的解法,当你了解这个DP转移路径 (前提这个路径是正确的),那么你的for也就大概浮现出来了;
我们假设dp[i][j]是字符串从i到j这个区间变成回文串所需最小花费,那么这个dp[i][j]可以从dp[i+1][j]·dp[i][j-1]·dp[i+1][j-1]这三个子结构所得到,那么你就是求dp[1][length]的最小值;
- dp[1][length]《==dp[2][length]·dp[1][length-1]·dp[2][length-1];
因为菜鸡很菜又很懒,就不往下推了
很轻松发现规律
简要介绍几个操作
- 你如果想得到将’a’换成’c’,有可能可以通过中转点’b’花费更少,所以这是一个简单最短路预处理,就用你们最擅长的floyd(不会的自行百度);//下面简称i位置的字符为i; j字符同理
- dp[i+1][j]和dp[i][j-1] :这意味着新加入一个字符,那么我们可以在相反位置加一个一样的字符或者删去这个字符使其变成回文串,那么加入和删去的时候会有加入一个中转字符’b’使其转化成你想要的字符;
– 还有一种情况为在反方向增加一个字符‘a’,让‘a’与‘i’都转化为‘o’ - dp[i+1][j-1]:这个要分情况讨论
– IF i==j dp[i][j]=dp[i+1][j-1]
– i 直接转化为 j ;亦或相反。
– i 转化为‘a’,j 也转化为‘a’。
情况结束,感觉文字表达不畅,看看代码应该能理解,不理解的评论区请问候我,我们一起进步。
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<math.h>
#define ll long long
using namespace std;
const int inf=1e14;
ll _a[30],_e[30],_c[30][30];
ll dp[55][55];
int main(){
char w[55];
scanf("%s",w+1);
int len=strlen(w+1);
int t;
scanf("%d",&t);
for(int i=0;i<26;i++){
for(int j=0;j<26;j++){
_a[i]=inf;
_c[i][j]=inf;
_e[i]=inf;
}
}
while(t--){
char p[10];
scanf("%s",p);
if(p[0]=='a'){
char a;
ll b;
cin>>a>>b;
_a[a-'a']=min(_a[a-'a'],b);
}
else if(p[0]=='e'){
char a;
ll b;
cin>>a>>b;
_e[a-'a']=min(_e[a-'a'],b);
}
else{
char a,b;
ll c;
cin>>a>>b>>c;
_c[a-'a'][b-'a']=min(_c[a-'a'][b-'a'],c);
}
}
for(int i=0;i<26;i++){
for(int j=0;j<26;j++){
for(int k=0;k<26;k++){
if(_c[j][k]>(_c[j][i]+_c[i][k])){
_c[j][k]=(_c[j][i]+_c[i][k]);
}
}
}
}
for(int i=len;i>=1;i--){
for(int j=i+1;j<=len;j++){
dp[i][j]=inf;
if(w[i]==w[j]){
dp[i][j]=dp[i+1][j-1];
}
dp[i][j]=min(dp[i][j],dp[i+1][j]+min(_e[w[i]-'a'],_a[w[i]-'a']));
dp[i][j]=min(dp[i][j],dp[i][j-1]+min(_e[w[j]-'a'],_a[w[j]-'a']));
dp[i][j]=min(dp[i][j],dp[i+1][j-1]+min(_c[w[i]-'a'][w[j]-'a'],_c[w[j]-'a'][w[i]-'a']));
for(int k=0;k<26;k++){
dp[i][j]=min(dp[i][j],dp[i+1][j]+_c[w[i]-'a'][k]+min(_e[k],_a[k]));
dp[i][j]=min(dp[i][j],dp[i][j-1]+_c[w[j]-'a'][k]+min(_e[k],_a[k]));
dp[i][j]=min(dp[i][j],dp[i+1][j]+_c[k][w[i]-'a']+_a[k]);
dp[i][j]=min(dp[i][j],dp[i][j-1]+_c[k][w[j]-'a']+_a[k]);
for(int o=0;o<26;o++){
if(_a[k]+_c[k][o]+_c[w[j]-'a'][o]>=0){
dp[i][j]=min(dp[i][j],dp[i][j-1]+_a[k]+_c[k][o]+_c[w[j]-'a'][o]);
}
if(_a[k]+_c[k][o]+_c[w[i]-'a'][o]>=0){
dp[i][j]=min(dp[i][j],dp[i+1][j]+_a[k]+_c[k][o]+_c[w[i]-'a'][o]);
}
}
dp[i][j]=min(dp[i][j],dp[i+1][j-1]+_c[w[i]-'a'][k]+_c[w[j]-'a'][k]);
}
}
}
if(dp[1][len]==2147483647){
printf("-1\n");
return 0;
}
ll op=dp[1][len];
printf("%lld\n",op);
}