pku 3280 Cheapest Palindrome DP

http://poj.org/problem?id=3280 

题意:

给定一个长度为m的字符串(都是小写字母) ,求通过添加或者删减若干个字符是原字符串变为回文串的最小代价(这里给出n个字符的删除与添加所需的代价,规定添加和删除的字符串都在给定的这n个字符中);

思路:

只能说自己dp太弱了,以后有时间一定要多练习dp。

这里对字符串从两端开始进行比较如果相同则dp[i][j] = dp[i + 1][j - 1],

如果不同则 dp[i][j] = min(dp[i + 1][j] + min(addv[str[i] -'a'],delv[str[i] - 'a']),dp[i][j - 1] + min(addv[str[j] -'a'],delv[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状态转移方程推出来了之后用记忆化搜索比较好些,于是就写了记忆化搜索:

View Code
#include <iostream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 2007
#define N 8
using namespace std;
const int inf = 99999999;

int dp[maxn][maxn];
char str[maxn];
int addv[27],delv[27];

int DP(int i,int j)
{
    if (dp[i][j] != 0) return dp[i][j];
    if (i >= j)
    {
        dp[i][j] =  0;
        return 0;
    }
    if (str[i] == str[j])
    dp[i][j] = DP(i + 1,j - 1);
    else
    dp[i][j] = min(DP(i + 1,j) + min(addv[str[i] - 'a'],delv[str[i] - 'a']),DP(i,j - 1) + min(addv[str[j] - 'a'],delv[str[j] - 'a']));
    return dp[i][j];
}
int main()
{
    //freopen("d.txt","r",stdin);
    int n,m,i;
    char ch[3];
    int a,b;
    scanf("%d%d",&n,&m);
    scanf("%s",str);
    for (i = 0; i < n; ++i)
    {
        scanf("%s%d%d",ch,&a,&b);
        addv[ch[0] - 'a'] = a;
        delv[ch[0] - 'a'] = b;
    }
    memset(dp,0,sizeof(dp));
    printf("%d\n",DP(0,m));
    return 0;
}

 

在附带一个dp的:

View Code
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define CL(a,num) memset(a,num,sizeof(a))
#define maxn 2007
#define N 8
using namespace std;
const int inf = 99999999;

int dp[maxn][maxn];
char str[maxn];
int addv[27],delv[27];


int main()
{
    //freopen("din.txt","r",stdin);
    int n,m,i,l,j;
    char ch[3];
    int a,b;
    scanf("%d%d",&n,&m);
    scanf("%s",str);
    for (i = 0; i < n; ++i)
    {
        scanf("%s%d%d",ch,&a,&b);
        addv[ch[0] - 'a'] = a;
        delv[ch[0] - 'a'] = b;
    }
    CL(dp,0);
    for (l = 0; l < m; ++l)
    {
        for (i = 0; i + l < m; ++i)
        {
            j = i + l;
            if (str[i] == str[j]) dp[i][j] = dp[i + 1][j - 1];
            else
            dp[i][j] = min(dp[i + 1][j] + min(addv[str[i] -'a'],delv[str[i] - 'a']),
                           dp[i][j - 1] + min(addv[str[j] -'a'],delv[str[j] - 'a']));
        }
    }
    printf("%d\n",dp[0][m - 1]);
    return 0;
}

转载于:https://www.cnblogs.com/E-star/archive/2012/08/12/2635225.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值