题目链接:
http://poj.org/problem?id=3280
题意:
给出一个字符串,要求将其修改成一个回文字符串,给出修改某种字母(添加或删除)的价值,求最小使其成为回文字符串的价值。
题解:
感觉是求最长回文子序列的变形,然而刚开始想着用类似于求最长回文子序列的方法,将字符串反转后与原字符串匹配最长公共子序列,在匹配的过程中来进行dp转移,但发现这样不行,因此还是借鉴了网上的方法用dp[i][j]表示处理完区间将i~j之间的字符串变为回文字符串的最小代价,那么很明显有转移方程
if (s[i-1]==s[j-1])
dp[i][j]=dp[i+1][j-1];
else
dp[i][j]=min(dp[i+1][j]+cost[ci],dp[i][j-1]+cost[cj]);
还有一点就是其实虽然给出的删除和修改是两个费用,但实际上每次修改时添加和删除的效果是一样的,因此每次都一定会取其中最小的费用,新开一个数组存起来就不用每次比较了。
代码:
#include<iostream>
#include<algorithm>
#include<stdio.h>
#include<string.h>
using namespace std;
int n,m,cost[2005],dp[2005][2005];
char s[2005];
int main()
{
scanf("%d%d",&n,&m);
scanf("%s",s);
for (int i=1;i<=n;i++)
{
char c[2];
int a1,a2;
scanf("%s%d%d",c,&a1,&a2);
cost[c[0]-'a']=min(a1,a2);
}
//for (int i=1;i<=m;i++)
//for (int j=1;j<=m;j++)
//dp[i][j]=1e9+7;
//for (int i=1;i<=m;i++)
//dp[i][i]=0;
//此题中不需要给dp初始化,因为每一个(i,j)(i<j)只会被枚举到一次
for (int i=m;i>=1;i--)
for (int j=i+1;j<=m;j++)
if (s[i-1]==s[j-1])
//如果j=i+1时我们发现i+1>j-1有没有必要特判一下呢,并不需要。
dp[i][j]=dp[i+1][j-1];
else
{
int ci=s[i-1]-'a';
int cj=s[j-1]-'a';
dp[i][j]=min(dp[i+1][j]+cost[ci],dp[i][j-1]+cost[cj]);
}
printf("%d\n",dp[1][m]);
}