poj3280Cheapest Palindrome<DP>

链接: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 }
原文地址:https://www.cnblogs.com/jian1573/p/2861107.html