longpo的回文——题解

题目大意

给出一个字符串,用三种操作使其变成回文串
* 添加 a d d x c o s t
* 删除 e r a s e x c o s t
* 改变 c h a n g e x c o s t

DP全靠猜,第一次基本猜对字符串DP
定义区间DP
F [ L ] [ R ] 表示将L~R变为回文串的最小代价
——然后?我们倒着思考,显然 F [ i ] [ i ] = 0 ,然后在两边添加字母使其回文:
所以 F [ L ] [ R ] = m i n ( F [ L ± 1 ] [ R ± 1 ] ) + c o s t

然后考虑代价——代价肯定不能简单地使用读入的:
比如,要删除一个字母,有可能先将其变成其他的字母,再删会“便宜”很多
所以我们要先 f l o y e d 预处理一趟,更新出真真正正的最小代价刷DP

for(int k='a';k<='z';k++)
   for(int i='a';i<='z';i++)
       for(int j='a';j<='z';j++) if(cst[i][k]+cst[k][j]<cst[i][j]) cst[i][j]=cst[i][k]+cst[k][j];//改变 
for(int i='a';i<='z';i++)
   for(int j='a';j<='z';j++) if(cst[i][j]+cl[j]<cl[i]) cl[i]=cst[i][j]+cl[j];//删除 
for(int i='a';i<='z';i++)
   for(int j='a';j<='z';j++) if(add[j]+cst[j][i]<add[i]) add[i]=add[j]+cst[j][i];//添加 

另外,刷DP时要记得26个字母都要试试改变,具体见代码——

#include<cstdio>
#include<cstring>
#define LL long long
using namespace std;
const int maxn=55;
const LL INF=(1e15);
int n,m;char s[maxn],flg[maxn];bool is[maxn][maxn];LL f[maxn][maxn],cst[150][150],add[150],cl[150];bool vis[maxn][maxn];
LL DFS(int L,int R){
    if(vis[L][R]||f[L][R]!=INF) return f[L][R];
    vis[L][R]=1;
    if(L>R) return INF;
    if(is[L][R]) return f[L][R]=0;//回文直接弹 
    LL& ans=f[L][R],tep;

    tep=DFS(L+1,R);
    if(tep+cl[s[L]]<ans) ans=tep+cl[s[L]];//把左端干掉 
    if(tep+add[s[L]]<ans) ans=tep+add[s[L]];//在右端添加一个 
    for(int k='a';k<='z';k++) if(tep+cst[s[L]][k]+add[k]<ans) ans=tep+cst[s[L]][k]+add[k];//左端变,右端加 

    tep=DFS(L,R-1);
    if(tep+cl[s[R]]<ans) ans=tep+cl[s[R]];//把右端干掉 
    if(tep+add[s[R]]<ans) ans=tep+add[s[R]];//在左端添加一个 
    for(int k='a';k<='z';k++) if(tep+cst[s[R]][k]+add[k]<ans) ans=tep+cst[s[R]][k]+add[k];//右端变,左端加 

    tep=DFS(L+1,R-1);
    if(tep+cl[s[R]]+cl[s[L]]<ans) ans=tep+cl[s[R]]+cl[s[L]];//两端都干掉 
    for(int k='a';k<='z';k++) if(cst[s[L]][k]+cst[s[R]][k]+tep<ans) ans=cst[s[L]][k]+cst[s[R]][k]+tep;//两端都变 

    return ans;
}
int main(){
    scanf("%s%d",s+1,&m),n=strlen(s+1);
    for(int i=1;i<=n;i++)
      for(int j=i+1;j<=n;j++) f[i][j]=INF;
    for(int i='a';i<='z';i++){
        for(int j='a';j<='z';j++) if(i!=j) cst[i][j]=INF;
        cl[i]=add[i]=INF;
    }
    for(int i=1;i<=n;i++)
      for(int j=i;j<=n;j++){
        bool vis=1;
        for(int k1=i,k2=j;k1!=k2&&vis;k1++,k2--) if(s[k1]!=s[k2]) vis=0;
        is[i][j]=vis;//判断回文 
      }
    for(int i=1;i<=m;i++){
        scanf("%s",flg);
        char A,B;int x;
        if(flg[0]=='c') scanf("%*c%c%*c%c%d",&A,&B,&x),cst[A][B]=x<cst[A][B]?x:cst[A][B];
          else{
            scanf("%*c%c%d",&A,&x);
            if(flg[0]=='a') add[A]=x<add[A]?x:add[A];else cl[A]=x<cl[A]?x:cl[A];
          }
    }
    for(int k='a';k<='z';k++)
      for(int i='a';i<='z';i++)
        for(int j='a';j<='z';j++) if(cst[i][k]+cst[k][j]<cst[i][j]) cst[i][j]=cst[i][k]+cst[k][j];//改变 
    for(int i='a';i<='z';i++)
      for(int j='a';j<='z';j++) if(cst[i][j]+cl[j]<cl[i]) cl[i]=cst[i][j]+cl[j];//删除 
    for(int i='a';i<='z';i++)
      for(int j='a';j<='z';j++) if(add[j]+cst[j][i]<add[i]) add[i]=add[j]+cst[j][i];//添加 
    printf("%lld\n",DFS(1,n)==INF?-1:DFS(1,n));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42403731/article/details/81808447