区间dp系列

牛牛的回文串

题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define ll long long
/*
 解决一个回文串可以发现,添加一个字符和删除一个字符是等价的,删除一个字符相当于在另一侧添加一个字符,所以只需要计算删除一个字符
 所需要的的代价即可,删除一个字符可以用过
 1. 先将该字符change成另一个字符再删除
 2. 添加任意一个字符再修改成该字符相当于添加
 3. 将该字符替换成另一个字符c在通过添加另一个字符
 4. 将该字符替换成另一个字符c,再任意添加一个字符c1,再将c1替换成c
 可以先用floyd来求出任意两个字符转换的最小代价,再去考虑上述四种情况求出cost
 然后使用dp来解决问题
 dp状态转移方程如下
 dp[i][j]表示i~j是回文串的最小代价
 从后往前和从前往后其实都一样
 然后考虑dp[i+1~n]dp[j-1~n]都是已经处理好的了,
 所以
 if(s[i]==s[j])dp[i][j]=dp[i+1][j-1];
 考虑不相等的情况
 1. 可以将s[i]删除val =  dp[i+1][j] + cost[s[i]-'a']
 2. 可以将s[j]删除val =  dp[i][j-1] + cost[s[j]-'a']
 3. 可以将s[i],s[j] 替换成某个字符 k
 4. 可以将s[i]替换成s[j],或者s[j]替换成s[i]
 */
const int N = 60;
ll dp[N][N];
ll cost[30], change[30][30];
ll cost_erase[30], cost_add[30];
const ll INF = 1e15 + 10;
char s[N];
void floyd() {
    for (int k = 0; k < 26; k++) {
        for (int i = 0; i < 26; i++) {
            for (int j = 0; j < 26; j++) {
                change[i][j] = min(change[i][j], change[i][k] + change[k][j]);
            }
        }
    }
    for (int i = 0; i < 26; i++) {
        for (int j = 0; j < 26; j++) {
            cost[i] = min(cost[i], min(cost_add[i], cost_erase[i]));

            cost[i] = min(cost[i], change[i][j] + min(cost_add[j], cost_erase[j]));

            cost[i] = min(cost[i], cost_add[j] + change[j][i]);

            for (int k = 0; k < 26; k++) {
                cost[i] = min(change[i][j] + cost_add[k] + change[k][j], cost[i]);
            }
        }
    }
}
int main() {
    scanf("%s", s + 1);
    int m;
    scanf("%d", &m);
    for (int i = 0; i < 26; i++) {
        cost_erase[i] = cost_add[i] = cost[i] = INF;
        for (int j = 0; j < 26; j++) {
            change[i][j] = INF;
        }
    }
    while (m--) {
        char op[20], ch[2], ch1[2];
        int val;
        scanf("%s", op);
        if (op[0] == 'e') {
            scanf("%s%d", ch, &val);
            cost_erase[ch[0] - 'a'] = min(cost_erase[ch[0] - 'a'], 1ll * val);
        }
        if (op[0] == 'a') {
            scanf("%s%d", ch, &val);
            cost_add[ch[0] - 'a'] = min(cost_add[ch[0] - 'a'], 1ll * val);
        }
        if (op[0] == 'c') {
            scanf("%s%s%d", ch, ch1, &val);
            change[ch[0] - 'a'][ch1[0] - 'a'] =
                min(change[ch[0] - 'a'][ch1[0] - 'a'], 1ll * val);
        }
    }
    floyd();
    int len = strlen(s + 1);
    for (int i = len; i >= 1; i--) {
        for (int j = i + 1; j <= len; j++) {
            dp[i][j] = INF;
            if (s[i] == s[j])
                dp[i][j] = dp[i + 1][j - 1];

            dp[i][j] = min(dp[i][j], dp[i][j - 1] + cost[s[j] - 'a']);

            dp[i][j] = min(dp[i][j], dp[i + 1][j] + cost[s[i] - 'a']);

            dp[i][j] = min(dp[i][j], dp[i + 1][j - 1] + min(change[s[i] - 'a'][s[j] - 'a'], change[s[j] - 'a'][s[i] - 'a']));

            for (int k = 0; k < 26; k++) {
                dp[i][j] = min(dp[i][j], dp[i + 1][j - 1] + change[s[i] - 'a'][k] + change[s[j] - 'a'][k]);
            }
        }
    }
    if (dp[1][len] == INF)
        printf("-1\n");
    else
        printf("%lld\n", dp[1][len]);
}

题解:我太菜了,解不动,去看dl的解释吧……题解链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值