POJ 3280 Cheapest Palindrome [DP]

题意:给一个字符串,通过删除字符或者添加字符将它变成回文串。给出删除或添加每一个字符的费用,求最小费用。

思路:刚开始学习dp,觉得dp最重要的就是发现问题与子问题的递推关系了。

这里用dp[i][j]表示字符串从i到j变成回文串的最小花费。假设id[i] = id[j],则dp[i][j] = dp[i+1][j-1]。

如果不相等,有dp[i][j] = min(dp[i+1][j] + cost[ id[i]-'a' ], dp[i][j-1] + cost[ id[j]-'a' ])。具体分析如下

假设i到j构成的子串为akkk...kkkkd,有如下四种选择将其变成回文串:

1.   将位于i的字符a删掉,将剩下i+1到j子串变成回文串。花费: cost_delete['a'] + dp[i+1][j]。

2.   将i+1到j子串变成回文串后,在j后面添加一个字符a,则该串成为回文串。 花费:cost_add['a'] + dp[i+1][j]。

3.   将位于j的字符d删掉,将剩下i到j-1子串变成回文串。花费: cost_delete['d'] + dp[i][j-1]。

4.   将i到j-1子串变成回文串后,在i前面添加一个字符d, 则该串成为回文串。 花费:cost_add['d'] + dp[i][j-1]。

dp[i][j]等于上述四种情况的最小值。注意到,情况1与2,情况3与4,可以将某一字符删除或者添加两种操作中花费的最小值表示为cost[],便得到前面的递推公式。

更多细节见代码注释。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<algorithm>
 4 using namespace std;
 5 char id[2003];
 6 int cost[27], dp[2003][2003];
 7 int main()
 8 {
 9     int n, m;
10     scanf("%d%d",&n,&m);
11     scanf("%s",id);
12     while (n--)
13     {
14         int a, b;
15         char c;
16         getchar();
17         scanf("%c %d%d",&c,&a,&b);
18         cost[c-'a'] = min(a, b);
19     }
20     memset(dp, 0, sizeof(dp));
21     for (int len = 2; len <= m; len++)//枚举子串长度
22         for (int i = 0; i + len - 1 < m; i++)//枚举子串起点
23         {
24             if (id[i] == id[i+len-1]) dp[i][i+len-1] = dp[i+1][i+len-2];
25             else dp[i][i+len-1] = min(dp[i+1][i+len-1] + cost[id[i]-'a'], dp[i][i+len-2] + cost[id[i+len-1]-'a']);
26         }
27     printf("%d", dp[0][m-1]);
28     return 0;
29 }

 

 

posted @ 2013-08-16 17:52  fenshen371  阅读(181)  评论(0编辑  收藏  举报