poj 3280 Cheapest Palindrome 解题报告 最长回文子序列变形

题目大意:

给定一个长M,由N个小写字母构成的字符串,每插入或者删除一个字符都需要一定的花费,增删字符的花费不同,问怎样可以使这个字符串变成一个回文串,且花费最小。


思路:

由于在一头加上一个字母和在另一头减去一个字母对于得到一个回文串来讲效果是等价的,但是这两种操作的花费可能不同,因此,需要添加或者删除字母的时候只需要选择这两种里面花费较小的那一种。假设已经将区间[i,j]整理成为一个回文串,当DP到区间[i,j+1]时,我们可以在i-1的位置添加一个s[j+1]字符,或者将在j+1处的字符删除,得到一个新的回文串。因此从[i,j]到[i,j+1]的状态转移只相差一个增删s[j+1]字符的最小花费。

接下来核心代码和求最长回文子序列的模板代码就比较类似了。

附最长回文子序列基础教程:

http://www.cnblogs.com/AndyJee/p/4465696.html


代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<limits.h>
#include<string.h>
using namespace std;

string s;
int n,m;
int cost[30];
int dp[2006][2006];//dp[i][j] 存储从 i 到 j 这一段转换成回文子序列需要的最小花费 

int main(){//14584k,32ms
	ios::sync_with_stdio(false);//将stdio解除绑定,使得cin与scanf效率相差无几 
	int a,b;
	char t;
	while(cin>>n>>m){
		cin>>s;
		while(n--){
			cin>>t>>a>>b;
			cost[t-'a']=min(a,b);
		}
		for(int i=m-1;i>=0;--i){
			dp[i][i]=0;
			for(int j=i+1;j<m;++j){
				if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
				else dp[i][j]=min((dp[i+1][j]+cost[s[i]-'a']),(dp[i][j-1]+cost[s[j]-'a']));
			}
		}
//i和j先循环哪个都行,只要保证从下向上(i递减) && 从左向右(j递增) && i<=j 
//		for(int j=0;j<m;++j){
//			dp[j][j]=0;
//			for(int i=j-1;i>=0;--i){
//				if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1];
//				else dp[i][j]=min((dp[i+1][j]+cost[s[i]-'a']),(dp[i][j-1]+cost[s[j]-'a']));
//			}
//		} 
		printf("%d\n",dp[0][m-1]);
	}
	return 0;
}


为了节省空间,可以只开两行的数组,因为每次更新只需要相邻行的数据。

参考链接:

http://www.2cto.com/kf/201311/258795.html

代码:

#include<stdio.h>
#include<iostream>
#include<math.h>
#include<algorithm>
#include<limits.h>
#include<string.h>
using namespace std;

string s;
int n,m,ans;
int cost[30];
int dp[2][2006];

int main(){//732k,16ms 
	ios::sync_with_stdio(false);
	int a,b;
	char t;
	while(cin>>n>>m){
		cin>>s;
		while(n--){
			cin>>t>>a>>b;
			cost[t-'a']=min(a,b);
		}
		int now=0;
		for(int i=m-1;i>=0;--i,now=!now){
			dp[now][i]=0;
			for(int j=i+1;j<m;++j){//每次更新这一行剩下的 
				if(s[i]==s[j]) dp[now][j]=dp[!now][j-1];
				else dp[now][j]=min((dp[!now][j]+cost[s[i]-'a']),(dp[now][j-1]+cost[s[j]-'a']));
			}
		}
		if(m%2) ans=dp[0][m-1];//字符串长度为奇数,则now进行偶数次反转,结果在第0行 
		else ans=dp[1][m-1];//字符串长度为偶数,结果在第1行 
		printf("%d\n",ans);
	}
	return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值