题意:给你一个字符串和每个字符添加和删除的花费,可以在任一位置添加或者删除字符,求使字符串变成回文串的最小花费。
思路: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
#include
#include
#include
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
#include
#include
#include
#include
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