题意:一个字母序列长度为 m ,由 n 个字母组成,每个字母有两个花费:① 删掉这个字母;② 加上这个字母。问把这个序列变成一个回文序列的最小花费。
题解:F[ i ][ j ] 表示 i - j 这一段变成回文串的最小花费。
当序列 s[ i ] == s[ j ] 时,不需要任何花费:F[ i ][ j ] = F[ i + 1][ j - 1];
否则:① -> 加上或减去第 i 个字母,F[ i + 1 ][ j ] + w[s[ i ]];
② -> 加上或减去第 j 个字母,F[ i ][ j - 1 ] + w[s[j]];
(因为加上或删去一个字母是等效的,那么代价在读入的时候取最小就可以了)
CODE:
/*
Author: JDD
PROG: poj3280 Cheapest Palindorome
DATE: 2015.10.9
*/
#include <cstdio>
#define REP(i, s, n) for(int i = s; i <= n; i ++)
#define REP_(i, s, n) for(int i = n; i >= s; i --)
#define MAX_N 2005
using namespace std;
int n, m, F[MAX_N][MAX_N], w[30];
char s[MAX_N];
inline int read()
{
char c = getchar();
while(!(c >= '0' && c <= '9')) c = getchar();
int ret = 0;
while(c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
return ret;
}
#define min(a, b) (a < b ? a : b)
inline void init()
{
n = read(); m = read();
scanf("%s", s + 1);
getchar();
char c; int x, y;
REP(i, 1, n){
scanf("%c%d%d", &c, &x, &y);
getchar();
w[c - 'a' + 1] = min(x, y);
}
}
inline void doit()
{
REP_(i, 1, m - 1) REP(j, i + 1, m){
if(s[i] == s[j]) F[i][j] = F[i + 1][j - 1];
else F[i][j] = min(F[i + 1][j] + w[s[i] - 'a' + 1], F[i][j - 1] + w[s[j] - 'a' + 1]);
}
printf("%d\n", F[1][m]);
}
int main()
{
init();
doit();
return 0;
}