poj3280Cheapest Palindrome

链接: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]}

View Code
 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 }

 

你可能感兴趣的:(heap)