洛谷P2890 [USACO07OPEN]便宜的回文Cheapest Palindrome
题目链接:
题目分析:
玄学\(dp\)
设\(val[s[i] - 'a' + 1]\)表示字母\(s[i]\)的花费
首先发现对于一个已经回文了的串\(s[i, j]\),在\(s[i - 1]\)的位置上删去和在\(s[j + 1]\)的位置上加上本质上是一样的,所以\(val[s[i] - 'a' + 1]\)直接取增删的最小即可
设\(dp[i][j]\)表示把\(s[i, j]\)变成回文串的最小代价,初始化所有花费为\(INF\),\(dp[i][i] =0\),如果有\(s[i] == s[i + 1]\),那么有\(dp[i][j + 1] = 0\)
然后是玄学的转移方程,稍微分情况讨论一下:
- 若有\(s[i - 1] == s[j + 1]\),则\(dp[i - 1][j + 1] = min(dp[i - 1][j + 1], dp[i][j])\)
- \(dp[i - 1][j] = min(dp[i - 1][j], dp[i][j] + val[s[i - 1] - 'a' + 1])\)
- \(dp[i][j + 1] = min(dp[i][j + 1], dp[i][j] + val[s[j + 1] - 'a' + 1])\)
答案即为\(dp[1][m]\)
代码:
#include<bits/stdc++.h>
#define N (2000 + 10)
using namespace std;
inline int read() {
int cnt = 0, f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -f; c = getchar();}
while (isdigit(c)) {cnt = (cnt << 3) + (cnt << 1) + c - '0'; c = getchar();}
return cnt * f;
}
const int INF = 1000000000 + 7;
int n, m, x, y;
char s[N], c;
int val[30], dp[N][N];
int main() {
n = read(), m = read();
scanf("%s", s + 1);
for (register int i = 1; i <= n; i++) {
cin >> c;
x = read(), y = read();
val[c - 'a' + 1] = min(x, y);
}
for (register int i = 0; i <= m; ++i)
for (register int j = 0; j <= m; ++j) dp[i][j] = INF;
for (register int i = 1; i <= m; i++) {
dp[i][i] = 0;
if (s[i] == s[i + 1]) dp[i][i + 1] = 0;
}
for (register int l = 0; l <= m; ++l)
for (register int i = 1; i <= m; ++i) {
int j = i + l;
if (s[i - 1] == s[j + 1]) dp[i - 1][j + 1] = min(dp[i - 1][j + 1], dp[i][j]);
dp[i - 1][j] = min(dp[i - 1][j], dp[i][j] + val[s[i - 1] - 'a' + 1]);
dp[i][j + 1] = min(dp[i][j + 1], dp[i][j] + val[s[j + 1] - 'a' + 1]);
}
printf("%d", dp[1][m]);
return 0;
}