POJ 3280 Cheapest Palindrome【DP】
题意:对一个字符串进行插入删除等操作使其变成一个回文串,但是对于每个字符的操作消耗是不同的。求最小消耗。
思路:
我们定义dp [ i ] [ j ] 为区间 i 到 j 变成回文的最小代价。
那么对于dp【i】【j】有三种情况
首先:对于一个串如果s【i】==s【j】,那么dp【i】【j】=dp【i+1】【j-1】
其次:如果dp【i+1】【j】是回文串,那么dp【i】【j】=dp【i+1】【j】+min(add【i】,del【i】);
最后,如果dp【i】【j-1】是回文串,那么dp【i】【j】=dp【i】【j-1】 + min(add【j】,del【j】);
显然是一个dp问题,可以设定状态dp[i][j]表示从i~j这一段变成回文的最小消耗,显然状态转移就是
if(s[i]==s[j])
dp[i][j]=dp[i+1][j-1];
else
dp[i][j]=min(dp[i+1][j]+w[s[i]-'a'],dp[i][j-1]+w[s[j]-'a']);
首先明确的就是如果已经将区间[i,j]整理成为一个回文串(不管中间有多少个字符或者是以什么字符开头或者结尾),当DP到区间[i,j+1]时,我们可以在i-1的位置添加一个str[j+1]字符,或者将在j+1处的字符删除,得到一个新的回文串,而且我们这两步操作都没有借助或者影响区间[i,j]的情况。
因此虽然删除和插入不同,但是这两种操作是等价的,这头加和那头减是一样的,那我们就可以将添加或者删除整合在一起,对字符str[j+1]的操作就按照添加和删除中花费最小的一个计算
另一个注意点是两重循环的方向,起点i应当趋0,终点j应当趋M
#include <iostream> #include <cstdio> #include <queue> #include <math.h> #include <string.h> #include <string> #include <algorithm> using namespace std; int w[30],n,m,dp[2005][2005]; char s[2005],ch; int main() { int x,y; while(scanf("%d%d",&m,&n)!=EOF) { getchar(); scanf("%s",s); getchar(); for(int i=0;i<m;i++) { scanf("%ch",&ch); scanf("%d%d",&x,&y); getchar(); w[ch-'a']=min(x,y); } for(int i=n-1;i>=0;i--) for(int j=i+1;j<n;j++) { if(s[i]==s[j])dp[i][j]=dp[i+1][j-1]; else dp[i][j]=min(dp[i+1][j]+w[s[i]-'a'],dp[i][j-1]+w[s[j]-'a']);//去头,去尾 } printf("%d\n",dp[0][n-1]); } return 0; }