题目大意:长度为M的字符串,可以增加或删除字符,使其构成回文,增加或删除不同的字符有不同的花费,求最小花费。
思路:其实dp很难逃出3种思路:
1、一维线性dp:每次考虑i时,选择最优子问题要么在i-1,要么在1...i-1里;
2、二维线性dp:考虑(i,j)子问题时,选择最优子问题要么在(i+1,j)、(i,j-1),要么在i<= k <=j,在k里;
3、树形dp:考虑i节点最优时,选择子节点最优,一般融合了01背包dp的双重dp。
上面3中模式也是我在做题后才发现的。
这个dp题其实就可以仿照第2中思路。
假设一个字符串Xx....yY;对于求这个字符串怎么求呢?
分4中情况讨论:
1、去掉X,取x....yY回文;
2、去掉Y,取Xx....y回文;
3、在右边加上X,取Xx....yYX回文;
4、在左边加上Y,取YXx....yY回文。
至于去掉X、Y肯定没有第1、2中情况合算;加上X、Y肯定没有第3、4中情况合算。
因此令dp[i][j]为i...j要变成回文字符串的最小代价。
方程:
dp[i][j] = min{ dp[i+1][j] + {去掉X的代价},dp[i+1][j] + {加上X的代价},
dp[i][j-1]+ {去掉Y的代价},dp[i][j-1] +{加上Y的代价}};
其实分析发现,对于X而言,只要去 去掉 和加上 X 最小代价就行(因为前面dp串一样),Y同理。
因此最后得出:
dp[i][j] = min{ dp[i+1][j] +min{ {去掉X的代价}, {加上X的代价}},
dp[i][j-1]+min{ {去掉Y的代价}, {加上Y的代价}}};
dp时候还有些注意事项:
比如当X和Y字符一样时,则在dp时必须先为x...y的最小代价。
#include <stdio.h>
#include <string.h>
#include <memory.h>
#define MAXSIZE 2048
int add[30];
int del[30];
char str[MAXSIZE];
int dp[MAXSIZE][MAXSIZE];
int min_num(int a,int b,int c,int d) {
int temp;
temp=a;
if (b<temp)
temp=b;
if (c<temp)
temp=c;
if (d<temp)
temp=d;
return temp;
}
int main()
{
int n,m,i,j;
char c;
scanf("%d%d",&n,&m);
getchar();
memset(dp,0,sizeof(dp));
scanf("%s",&str);
for (i=0;i<n;i++) {
getchar();
scanf("%c",&c);
scanf("%d%d",&add[c-'a'],&del[c-'a']);
}
//i为子串的末尾坐标,j为子串的起始坐标
for (i=0;i<m;i++) {
for (j=i-1;j>=0;j--) {
if (str[i]==str[j])
dp[j][i]=dp[j+1][i-1];
else
dp[j][i]=min_num(dp[j+1][i]+add[str[j]-'a'],dp[j+1][i]+del[str[j]-'a'],dp[j][i-1]+add[str[i]-'a'],dp[j][i-1]+del[str[i]-'a']);
}
}
printf("%d\n",dp[0][m-1]);
return 0;
}