牛牛的回文串

本文介绍了一道关于使用动态规划求解回文串转换的算法题,涉及区间DP和最短路预处理。题目要求在有限代价下将字符串转化为回文串,允许增加、删除和更改字符。通过Floyd算法处理最短路径,然后利用动态规划状态转移求解,最终得出最小代价。代码实现包括预处理和DP状态转移细节。
摘要由CSDN通过智能技术生成

牛牛的回文串//区间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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值