题目链接:
[POJ 3280]Cheapest Palindrome[DP]
题意分析:
有一个长度为m的字符串,全都为小写字母,给出串中每个字母删除和插入的代价,问:要构成回文串,最少需要多少代价?
解题思路:
设dp[i][j]为字符串s[i ~ j]变成回文串需要的最小代价。那么有转移方程:
dp[i][j] = dp[i + 1][j - 1] (s[i] == s[j]);
否则 dp[i][j] 由dp[i + 1][j]添加或者删除s[i],由dp[i][j - 1]添加或者删除s[j]得来。前者新增字符为s[i],后者新增字符为s[j],而s[i + 1][j] 和 s[i][j - 1]均已经为回文,所以这么转移。
个人感受:
状态有了,可是我一直纠结内部的具体细节,比如它删了,它添加了,那么后继状态的删除添加不就要考虑前面状态了吗?其实并不用考虑这么多,因为转移二已经把所有的添加删除都考虑了。
具体代码如下:
#include<cstdio>
#include<iostream>
#define ll long long
using namespace std;
const int MAXN = 2e3 + 111;
ll dp[MAXN][MAXN];
char sh[MAXN];
int s[MAXN];
ll ad[30], del[30];
int main()
{
int n, m; scanf("%d%d%s", &n, &m, sh);
for (int i = 0; i < m; ++i) s[i] = sh[i] - 'a';
char c[2];
int a, b;
for (int i = 0; i < n; ++i) {
scanf("%s%d%d", c, &a, &b);
ad[c[0] - 'a'] = a;
del[c[0] - 'a'] = b;
}
for (int i = m - 1; i >= 0; --i) {
for (int j = i + 1; j < m; ++j) {
if (s[i] == s[j]) dp[i][j] = dp[i + 1][j - 1];
else {
dp[i][j] = min(dp[i + 1][j] + min(ad[s[i]], del[s[i]]),
dp[i][j - 1] + min(ad[s[j]], del[s[j]]));
}
}
}
printf("%lld\n", dp[0][m - 1]);
return 0;
}