poj 3280 Cheapest Palindrome (DP)

题意:有一个由n个小写字母组成的,长度为m的字符串,可以对其通过增加字符或者删除字符来使其变成回文。而增加或者删除字符都有一个花费,求解使该字符串变成回文的最小花费。

 

思路:DP。dp[i][j]表示串str[i~j]变成回文的最小代价,故状态转移方程为:

当str[i] == str[j]时 dp[i][j] = dp[i+1][j-1];
当str[i] != str[j]时 dp[i][j] = min( dp[i+1][j]+add[str[i]-'a'], dp[i][j-1]+add[str[j]-'a'],

                                     dp[i+1][j]+del[str[i]-'a'], dp[i][j-1]+del[str[j]-'a']);

 

其实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....y回文。

至于去掉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的最小代价。

源代码:(14056K, 79MS)
#include<iostream>
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int MAX = 2005;
 
int dp[MAX][MAX];
int add[30], del[30];
 
int main(){
    int n, m, i, j, len;
    char c, str[MAX];
    cin >> n >> m >> str;
    for(i = 0; i < n; i ++){
        cin >> c;
        cin >> add[c-'a'] >> del[c-'a'];
    }
    for(len = 1; len < m; len ++)
        for(i = 0; i + len < m; i ++){
            j = i + len;
            if(str[i] == str[j]){
                dp[i][j] = dp[i+1][j-1];
            }else{
                dp[i][j] = min(
                    min(dp[i+1][j]+add[str[i]-'a'], dp[i][j-1]+add[str[j]-'a']),
                    min(dp[i+1][j]+del[str[i]-'a'], dp[i][j-1]+del[str[j]-'a']));
            }
        }
    cout << dp[0][m-1] << endl;
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值