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 }

 

 

你可能感兴趣的:(heap)