洛谷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;
}
posted @ 2019-09-07 09:49  kma_093  阅读(138)  评论(0编辑  收藏  举报