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 }