题目链接:http://poj.org/problem?id=3280
大致题意:给定一个字符串,字符串中的每个不同的字符都对应一个增加成本和删减成本,问将给定的字符串变成回文字符串所需要的最小成本。
思路:在《挑战程序设计》dp基础练习上看到的这道题,一开始看完题后没有头绪,想不出状态转移方程,后来自己想了一个枚举的方法,就是最后构成的回文串的对称中心一定是原串中的某个字符或者是某两个相邻字符的中间,敲了一个,仔细发现其中有些问题没法处理。 之后就去网上看了题解,自己又敲了一个,A掉了。 主要是注意一下几点:
1.删除和增加在本质上相同的,所以在记录成本是,只要取其中最小的即可。
2.状态转移方程: c[i]是原串,cost[i]表示花费成本,dp[i][j]表示子列i~j构成回文 的最小花费
3.最后在更新dp数组的时候一定要注意顺序,使子状态都是已经更新过的状态!!
code:
//poj 3280
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <stack>
#include <vector>
#include <assert.h>
using namespace std;
#define SIZE 2005
#define INF 100000000
int dp[SIZE][SIZE];
int cost[27];
char c[SIZE];
int M,N;
int Min(int a,int b)
{
return a<b? a:b;
}
int solve()
{
memset(dp,0,sizeof(dp));
for(int j=1;j<M;j++){
for(int i=j-1;i>=0;i--){
if(c[i]==c[j]) dp[i][j]=dp[i+1][j-1];
else dp[i][j]=Min(dp[i+1][j]+cost[c[i]],dp[i][j-1]+cost[c[j]]);
}
}
return dp[0][M-1];
}
int main()
{
while((scanf("%d%d",&N,&M))!=EOF){
scanf("%s",c);
getchar();
char mid;
int a,b;
for(int i=0;i<N;i++){
scanf("%c %d %d",&mid,&a,&b);
getchar();
cost[mid]=Min(a,b);
}
printf("%d\n",solve());
}
return 0;
}
最后加一点看题解博客时看到的dp小总结,摘过来分享一下:
其实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。
最后各位acmer加油,自己加油!!