POJ3280Cheapest Palindrome(区间DP)

跟踪所有奶牛可能是一项棘手的任务,所以农夫约翰安装了一个系统来自动化这个过程。他在每头奶牛身上安装了一个电子ID标签,系统会在奶牛经过扫描仪时读取它们。每个ID标签的内容目前是一个长度为 M(1 ≤ M ≤ 2,000)的字符串,由 N(1 ≤ N ≤ 26)个不同符号(即小写罗马字母表)组成。

奶牛是调皮的生物,有时会试图欺骗系统,倒着走。虽然一个ID为 "abcba" 的奶牛无论向哪个方向走都会读取相同的内容,但一个ID为 "abcb" 的奶牛可能会注册为两个不同的ID("abcb" 和 "bcba")。

约翰希望更改奶牛的ID标签,使其无论奶牛向哪个方向走都能读取相同的内容。例如,"abcb" 可以通过在末尾添加 "a" 来变为 "abcba",使得ID是回文的(正向和反向读取内容相同)。将ID更改为回文的其他方法包括在开头添加三个字母 "bcb" 以得到ID "bcbabcb",或者删除字母 "a" 以得到ID "bcb"。可以在字符串的任何位置添加或删除字符,得到比原始字符串长或短的字符串。

不幸的是,由于ID标签是电子的,每个字符的插入或删除都有一个成本(0 ≤ cost ≤ 10,000),具体取决于要添加或删除的字符值。给定奶牛的ID标签内容和插入或删除每个字母的成本,找出更改ID标签以满足约翰要求的最小成本。将空ID标签视为满足正向和反向读取相同内容的要求。只能添加带有关联成本的字母到字符串中。

输入

Line 1: 两个用空格分隔的整数: NM
Line 2: 此行包含恰好 M 个字符,构成初始ID字符串
Lines 3..N+2: 每行包含三个用空格分隔的实体: 输入字母表中的一个字符和两个整数,分别是添加和删除该字符的成本。

输出

Line 1: 一行,一个整数,表示更改给定名称标签的最小成本。

示例

InputcopyOutputcopy
3 4
abcb
a 1000 1100
b 350 700
c 200 800
900

提示

如果我们在末尾插入一个 "a" 得到 "abcba",成本将为 1000。如果我们删除开头的 "a" 得到 "bcb",成本将为 1100。如果我们在字符串开头插入 "bcb",成本将为 350 + 200 + 350 = 900,这是最小成本。

题意:给你一串字符,给出每个字符删除以及添加的代价,求让这个字符串变成回文串的最小代价。

     考虑区间DP,状态表示dp[i][j] 表示区间i ~ j 变成回文串的最小代价,考虑状态转移,对于字符i

可以是i+1 ~ j的右边加上s [ j ] 形成回文串,也可以是删除s[i]形成回文串,对于 j 同理,当然要特殊情况s[ i ] = s[ j ],此时dp[ i ][ j ] = dp[ i + 1 ][ j - 1 ];最后写出状态转移方程即可:

dp[i][j] =\begin{cases} & \text{ max } (dp[i+1][j] + d[s[i]], dp[i+1][j] + a[s[i]])\\ & \text{ max } (dp[i][j-1] + a[s[j]], dp[i][j-1] + d[s[j]]) \\ & \text{ if } (s[i] == s[j] ) max(dp[i][j],dp[i+1][j-1]) \end{cases}

d表示删除该字母的代价,a表示增加

代码如下:

#include <stdio.h>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
using namespace std;
#define endl '\n'
typedef unsigned long long ull;
const int INF = 0x3f3f3f3f;
map<char,int>ma,md;
int min(int x,int y)
{
    if(x > y)   return y;
    return x;
}
int dp[2010][2010];
void solve()
{
    int m,n;
    cin >> m >> n;
    string s;
    cin >> s;
    s = ' ' + s;
    while(m--)
    {
        char x;
        int a,b;
        cin >> x >> a >> b;
        ma[x] = a;
        md[x] = b;
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i = 1;i <= n;i++)
    {
        dp[i][i] = 0;
    }
    for(int len = 2;len <= n;len++)
    {
        for(int i = 1;i + len - 1 <= n;i++)
        {
            int j = i + len - 1;
            dp[i][j] = min(dp[i][j],dp[i+1][j] + min(ma[s[i]],md[s[i]]));
            dp[i][j] = min(dp[i][j],dp[i][j - 1] + min(ma[s[j]],md[s[j]]));
            if(s[i] == s[j])
            {
                if(j == i+1)    dp[i][j] = 0;
                else    dp[i][j] = min(dp[i][j],dp[i+1][j-1]);
            }
        }
    }
    cout << dp[1][n] << endl;
    return;
}

signed main() 
{
    ios::sync_with_stdio(false),cin.tie(0);
    int tt;
    tt = 1;
    while(tt--)
    {
        solve();
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值