(经典)POJ-3280 回文串DP

题目大意:给定一个字符串S及其长度M与S所含有的字符种数N(最多26种小写字母),然后给定这N种字母Add与Delete的代价,求将S变为回文串的最小代价和。


题目链接:点击打开链接


分析:

这题算得上是一个很经典的模型了。先来说说最原始的模型,通过增添或者删除某些字母将一个字符串变为回文串,求最少增添或者删除次数,其实在这里增添和删除是没有区别的,我们只考虑删除就行了,对于这个模型通常有2种解法:

①将S反转得到S',然后求两者最长公共子串的长度L,再用S的长度减去L便是答案了

②用 dp[i][j] 表示 [i,j] 区间的串的最优解,

s[i]==s[j],则dp[i][j]=dp[i+1][j-1](请思考为什么)

否则 dp[i][j] = min (d[i+1][j] , dp[i][j-1]) + 1,因为s[i]!=s[j],所以必定要删去左右边界的某一个才能变成一个回文串,至于到底删去哪一个,当然是取 dp[i+1][j] 和 dp[i][j-1] 的MIN了

然后反观这个题,只是在add和delete的操作上附加了一个代价,所以这里的add和delete便不再等价了(当然是在add!=delete时),于是对于 [i+1,j] 和 [i,j] 之间的转换,当然要选择是在 [i+1,j] 上add一个字母变为 [i,j] 还是 在[i,j] 上delete一个字母变为 [i+1,j] 中的最优解了。

所以第一种方法显然不再适用,对于第二种方法,只要稍微改一下就行了

s[i]==s[j],则dp[i][j]=dp[i+1][j-1]仍然成立

否则 dp[i][j] = min  (   d[i+1][j] + min(add[s[i]],delete[s[i]])  ,   dp[i][j-1]  + min(add[s[j]],delete[s[j]]) )   注意写代码时别用min嵌套,会出错!


附上代码:

#include<iostream>
#include<algorithm>
using namespace std;
typedef pair<int, int> P;
#define ad first
#define de second
int n, m, dp[2005][2005];
char s[2005];
P ch[28];           //ch[ch-'a'].ad代表add一个ch的代价,ch[ch-'a'].de代表delete一个ch的代价
int main()
{
	cin >> n >> m >> s; 
	for (int i = 1; i <= n; i++)
	{
		char Ch;
		cin >> Ch;
		cin >> ch[Ch - 'a'].ad >> ch[Ch - 'a'].de;
	}
	for (int i = 1; i <= m; i++)
		dp[i][i] = 0;
	for (int i = 1; i < m; i++)             //注意for循环要实现从短串到长串的过渡,这里i代表长度为i+1
		for (int j = 0; j + i < m; j++)
			if (s[j] == s[j + i]) dp[j][j + i] = dp[j + 1][j + i - 1];
			else
			{
				int aa = dp[j + 1][j + i] + min(ch[s[j] - 'a'].ad, ch[s[j] - 'a'].de);
				int bb = dp[j][j + i - 1] + min(ch[s[j + i] - 'a'].ad, ch[s[j + i] - 'a'].de);
				dp[j][j + i] = min(aa, bb);
			}
	printf("%d\n", dp[0][m - 1]);
	return 0;
}

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值