洛谷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;
}
原文地址:https://www.cnblogs.com/kma093/p/11479295.html