Poj 3280 Cheapest Palindrome

题目大意:长度为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;
}


你可能感兴趣的:(c,ini)