题意:给你一个字符串和每个字符添加和删除的花费,可以在任一位置添加或者删除字符,求使字符串变成回文串的最小花费。
思路:dp[i][j]表示i~j区间变成回文串的最小花费,假设现在要求dp[i][j],已知比他小区间的所有最优值。
则 : dp[i][j] 在 dp[i][j]删除头部构成的回文串dp[i+1][j] + delete[ s[i] ] 和 dp[i][j]添加尾部构成的回文串dp[i+1][j] + add[ s[i] ] 中取最小值。
同理:dp[i][j] 在 dp[i][j]删除尾部构成的回文串dp[i][j-1] + delete[ s[j] ] 和 dp[i][j]添加头部构成的回文串dp[i][j-1] + add[ s[j] ] 中取最小值。
如果 : s[i] == s[j] , 则s[i][j]在s[i+1][j-1]基础上两端已经是一个回文了,dp[i][j] = min(dp[i][j],dp[i+1][j-1])
状态转移方程:
if(s[i] == s[j]) dp[i][j] = min(dp[i][j],dp[i+1][j-1])
dp[i][j] = min(dp[i][j],dp[i+1][j]+add[s[i]]);
dp[i][j]= min(dp[i][j],dp[i][j-1]+add[s[j]]);
代码:(以区间为循环条件)
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int add[200]; int dp[2009][2009]; char s[2009]; int main() { int N,M; scanf("%d%d",&N,&M); cin>>(s+1); for(int i = 1; i<=N; i++) { char ch; int a,b; cin>>ch>>a>>b; add[ch] = min(a,b); } for(int i = 1; i<=M; i++)dp[i][i] = 0; for(int l = 1; l<=M; l++) { for(int i = 1; i+l<=M+1; i++) { int j= i+l-1; dp[i][j] = 0x3f3f3f3f;//无穷大初始化必须写在这,很迷很迷 if(s[i]==s[j])dp[i][j] = dp[i+1][j-1]; dp[i][j]= min(dp[i][j],dp[i+1][j]+add[s[i]]); dp[i][j]= min(dp[i][j],dp[i][j-1]+add[s[j]]); } } printf("%d\n",dp[1][M]); return 0; }
不以区间为条件,要知道dp[i][j]就必须知道dp[i+1][j-1]一类,因此起点必须从后往前推,最后求的是dp[1][M]因此,是后面的小区间不断往前合并,代码:
#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<string> using namespace std; int n,m,dp[2009][2009],in[27],de[27]; char ch[2009]; int main() { scanf("%d%d",&n,&m); scanf("%s",ch); for (int i=1;i<=n;i++) { char c; cin>>c; int k1,k2; scanf("%d%d",&k1,&k2); in[c-'a']=k1;de[c-'a']=k2; } for (int i=m-1;i>=0;i--) //起点后往前推 { dp[i][i]=0; for (int j=i+1;j<m;j++) //终点往后推 { dp[i][j]=0x3f3f3f3f;//因为找最小的,别忘了开始时置为无穷大 if (ch[i]==ch[j]) dp[i][j]=dp[i+1][j-1]; dp[i][j]=min(dp[i][j],min(dp[i+1][j]+in[ch[i]-'a'],dp[i+1][j]+de[ch[i]-'a'])); dp[i][j]=min(dp[i][j],min(dp[i][j-1]+in[ch[j]-'a'],dp[i][j-1]+de[ch[j]-'a']));//三种情况 //printf("%d %d %d\n",i,j,dp[i][j]); } } printf("%d",dp[0][m-1]); return 0; }