
2.3 记录结果再利用的“动态规划” 基础的动态规划算法
字串S长M,由N个小写字母构成。欲通过增删字母将其变为回文串,增删特定字母花费不同,求最小花费。
定义dp[i][j]表示将原字串s的子字串s[i…j]变换成回文的最小花费,满足如下递推关系式:
dp[i][j] = min( dp[i + 1][j] + cost[s[i] - 'a'], // 比i, j少了一个首字母
dp[i][j - 1] + cost[s[j] - 'a']); // 比i, j少了一个尾字母
if (s[i] == s[j])
{
// 首尾相同,等于少了首尾
dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
}
注意此处的可以增加也可以删除是个局,取其花费最小值即可。因为如果在首尾增删字母x都可以使一个字串s[i…j]变成回文的话,当然选取花费小的。
另一个注意点是两重循环的方向,起点i应当趋0,终点j应当趋M。
#include <iostream>
#include <string>
using namespace std;
int cost[32];
int dp[2048][2048];
///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
int N, M;
cin >> N >> M;
string s;
cin >> s;
for (int i = 0; i < N; ++i)
{
char c;
int add_cost, delete_cost;
cin >> c >> add_cost >> delete_cost;
cost[c - 'a'] = min(add_cost, delete_cost);
}
for (int i = M - 1; i >= 0; --i)
{
for (int j = i + 1; j < M; ++j)
{
dp[i][j] = min( dp[i + 1][j] + cost[s[i] - 'a'], // 比i, j少了一个首字母
dp[i][j - 1] + cost[s[j] - 'a']); // 比i, j少了一个尾字母
if (s[i] == s[j])
{
// 首尾相同,等于少了首尾
dp[i][j] = min(dp[i][j], dp[i + 1][j - 1]);
}
}
}
cout << dp[0][M - 1] << endl;
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
system("out.txt");
#endif
return 0;
}
///////////////////////////End Sub//////////////////////////////////
知识共享署名-非商业性使用-相同方式共享:码农场 » POJ 3280 Cheapest Palindrome 题解 《挑战程序设计竞赛(第2版)》
码农场
for (int i = M – 1; i >= 0; –i) 为什么要从后面开始,我试了i从0开始会WA,为什么起点i应当趋0,终点j应当趋M?
==s
){//这里考虑两边删除相同字母,为什么不考虑两边加上相同字母
if(s