POJ 3280 Cheapest Palindrome 区间DP

POJ 3280 Cheapest Palindrome 区间DP

题意

给你一个长度为m的字符串,然后给你n种操作,操作都是一些对某个字母进行增加和减少时所需要的花费,我们需要用这些若干个操作来使得给的字符串变成一个回文字符串,问最小花费是多少?

解题思路

实话说,我第一次看到这个题想到了针对于回文串的Manacher算法,但是这个最优问题没有办法搞定啊,于是停摆了一段时间,知道上网查了查(还是菜,ε=ε=ε=┏(゜ロ゜;)┛),说是区间dp的入门题,害,我之前做的那些区间dp题的知识都还给学长了。废话不多说,下面开始进入正题。

首先说一下为什么是区间dp,因为我们需要一段一段区间地进行求解,通过合并小区间的最优解来得出整个大区间的最优解的dp算法。

首先我们定义dp[i][j]为使得区间i到区间j成为回文串的最小代价,那么就有下们这几种情况了:

  1. dp[i+1][j]表示区间i到区间j已经是回文串了的最小代价,那么对于s[i]这个字母,我们有两种操作,删除与添加,对应有两种代价,dp[i+1][j]+add[ss[i]-'a'], dp[i+1][j]+del[ss[i]-'a'],取这两种代价的最小值;

  2. dp[i][j-1]表示区间i到区间j-1已经是回文串了的最小代价,那么对于ss[j]这个字母,同样有两种操作,dp[i][j-1]+add[ss[j]-'a'],dp[i][j-1]+del[ss[j]-'a'],取最小值;

  3. 若是ss[i]==ss[j],dp[i+1][j-1]表示区间i+1到区间j-1已经是回文串的最小代价,那么对于这种情况,我们考虑dp[i][j]dp[i+1][j-1]的大小;

然后dp[i][j]取上面这些情况的最小值,其实我们可以提前对每一个字符是增加还是删除的操作做一个比较,我们存下最小值就行了。

代码实现

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double esp=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=2e3+7;
int n, m;
char ss[MAXN];
int dp[MAXN][MAXN];
int cost[26];
int main()
{
	cin>>n>>m;
	cin>>ss;
	char tmp[2];
	int a, b;
	for(int i=1; i<=n; i++)
	{
		cin>>tmp>>a>>b;
		cost[tmp[0]-'a'] = min(a, b);
	}
	int len = strlen(ss);
	for(int k = 1; k<=len; k++)//区间的长度
		for(int i=0; i+k-1 < len; i++)//i为左端点, j为右端点
		{
			int j = i + k - 1;
			if(i != j)
				dp[i][j] = inf;
           	else {
				if(ss[i] == ss[j])
					dp[i][j] = dp[i+1][j-1];
				else{
					dp[i][j] = min(dp[i+1][j]+cost[ss[i]-'a'], dp[i][j]);
					dp[i][j] = min(dp[i][j-1]+cost[ss[j]-'a'], dp[i][j]);	
				}
            }
		}
	cout<<dp[0][len-1]<<endl;
	return 0;
}

看了一下网上的代码,还有另一种实现区间dp的方式,第一次看到,也加上了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
using namespace std;
int n,m,dp[2009][2009],in[27],de[27];
char ch[2009];
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",ch);
	for (int i=1;i<=n;i++) 
	{
		char c;
		if (scanf("%c",&c)&&c=='\n') scanf("%c",&c);
		int k1,k2;
		scanf("%d%d",&k1,&k2);
		in[c-'a']=k1;de[c-'a']=k2;
	}
	for (int i=m-1;i>=0;i--)
	{
		dp[i][i]=0;
		for (int j=i+1;j<m;j++)
		{
			dp[i][j]=0x3f3f3f3f;//因为找最小的,别忘了开始时置为无穷大
			if (ch[i]==ch[j]) dp[i][j]=dp[i+1][j-1];
			dp[i][j]=min(dp[i][j],min(dp[i+1][j]+in[ch[i]-'a'],dp[i+1][j]+de[ch[i]-'a']));
			dp[i][j]=min(dp[i][j],min(dp[i][j-1]+in[ch[j]-'a'],dp[i][j-1]+de[ch[j]-'a']));//三种情况
			//printf("%d %d %d\n",i,j,dp[i][j]);
		}
	}
	printf("%d",dp[0][m-1]);
	return 0;
}
posted @ 2020-03-24 16:50  ALKING1001  阅读(106)  评论(0编辑  收藏  举报