题目概要:对于用字典序中前n个小写字母组成的串,付出一定的代价来插入or删除使其成为回文串的最小代价。
解题思路:首先对于最优解,要么是贪心要么是DP。这题是DP。设f[i][i+l]为将a[i]~a[i+l]变成回文的最小代价。方程式:
①若a[i]==a[i+l] f[i][i+l]=f[i+1][i+l-1]
②s1=f[i][i+l-1]+增加代价;s2=f[i][i+l-1]+删减代价;s3=f[i+1][i+l]+增加代价;s4=f[i+1][i+l]+删减代价;f[i][i+l]=min(min(s1,s2),min(s3,s4));
如何理解这个方程式呢?对于当前的串,最好的情况无异于是首尾相同(满足回文的基础条件)且里面也回文(①的情况)。如果不满足就只有进行调整。情况一为,a[i+1][i+l]回文,删去a[i]或增加一个与a[i]相同的字母即可。情况二则以a[i+l]作为待调整的字母,与一同理(②的情况)。这样一来就可以出答案了。
又及:此方法适用于大多数有关于回文的DP
#include<iostream> #include<cstdio> #include<fstream> #include<cmath> #include<algorithm> #include<cstring> using namespace std; int read(){ char ch; int res=0,f=1; ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-')f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ res=res*10+(ch-'0'); ch=getchar(); } return res*f; } int n,tot,ans,c[5005],s[5005],k; long long f[5005][5005]; char a[5005],ch; int main(){ k=read(); n=read(); for(int i=1;i<=n;++i){ cin>>a[i]; } for(int i=1;i<=k;++i){ cin>>ch; c[ch-'a']=read(); s[ch-'a']=read(); } for(int l=1;l<n;++l){ for(int i=1;i+l<=n;++i){ if(a[i]==a[i+l])f[i][i+l]=f[i+1][i+l-1]; else{ int s1=f[i][i+l-1]+c[a[i+l]-'a'], s2=f[i][i+l-1]+s[a[i+l]-'a'], s3=f[i+1][i+l]+c[a[i]-'a'], s4=f[i+1][i+l]+s[a[i]-'a']; f[i][i+l]=min(min(s1,s2),min(s3,s4)); } } } printf("%lld",f[1][n]); return 0; }