链接:http://poj.org/problem?id=3280
题意:
现在有一个由n个字符组成的长度为m的字符串,可以对其通过增加字符或者删除字符来使其变成回文字符串,而增加或者删除字符都有一个花费,
求解使该字符串变成回文所进行操作的最小花费;
思路:
dp3种基本思路:
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。
本题属于第二种, 对于每个串来说,只需考虑开始位置和结束位置即可.
1> 如果s[i]!=s[j],那么有四种操作可以使其变成回文:
a> 在dp[i+1][j]处添加或者删除字符s[i],所以这两种操作花费为:dp[i][j]=min{dp[i+1][j]+add[i],dp[i+1][j]+del[i]}
用cost[i]表示cost[i]=min(add[i],del[i]),则上述方程可以写成:dp[i][j]=dp[i+1]+cost[i]
b> 在dp[i][j-1]处添加或者删除字符s[j],所以这两种操作的花费为:dp[i][j]=dp[i][j-1]+cost[j]
2> 如果s[i]==s[j],那么有两种情况构成回文数,即字符串保持不变和删除s[i]和s[j],也就是选择dp[i][j]与dp[i+1][j-1]中的最小值.
综上所述:状态转移方程为:
if( s[i]!=s[j] ) dp[i][j]=min{dp[i+1][j]+cost[i],dp[i][j-1]+cost[j]}
if( s[i]==s[j] ) dp[i][j]=min{dp[i][j],dp[i+1][j-1]}
1 #include <iostream> 2 #include <cstdio> 3 #include <string> 4 #include <cstring> 5 #include <cmath> 6 #include <algorithm> 7 #include <cstdio> 8 using namespace std; 9 int N, L, a, b, cost[30], dp[2005][2005]; 10 char s[2005], c[2]; 11 int main( ) 12 { 13 while(scanf( "%d%d", &N, &L )!=EOF){ 14 scanf( "%s", s ); 15 for( int i=0; i<N; ++ i ){ 16 scanf( "%s%d%d",c, &a, &b ); 17 cost[c[0]-'a']=min( a, b ); 18 } 19 memset( dp, 0, sizeof dp ); 20 for( int i=L-1; i>=0; -- i ){// 起点 21 for( int j=i+1; j<L; ++ j){ // 终点 22 dp[i][j]=min(dp[i+1][j]+cost[s[i]-'a'],dp[i][j-1]+cost[s[j]-'a']); 23 if(s[i]==s[j]) 24 dp[i][j]=min(dp[i][j],dp[i+1][j-1]); 25 } 26 } 27 printf( "%d\n", dp[0][L-1] ); 28 29 } 30 return 0; 31 }