BZOJ4607 : [PA2015 Final]Edycja

顯然作完操做$2$後再作操做$1$。ui

創建一個$26$個點的有向圖,每一個點只有一條出邊,$i$->$j$表示$i$最終變成了$j$,邊權爲一開始是$i$,最後不是$j$的位置個數,若是$i\neq j$,則代價還要增長$c$。blog

對於每一個點貪心選取最小的出邊,若是沒有環,那麼此時就是最優解。io

不然,對於一個連通塊,若是它是環,那麼須要多付出$c$點代價,並且若是全部連通塊都是環或者孤立點,則不可能構造出這種圖。class

考慮從新決定每一個點的出邊,若是出現了原來貪心構造出的圖中不存在的環,那麼必定有一個點的出邊和一開始不一樣,由於一開始是貪心選最小,所以把那條邊改回最開始的出邊,答案不會增長,並且新環被破壞了。所以對於不是原來中的圖的環,必定存在一種方案與它代價相同,且不存在這個環。im

因而設$f[i][S][j]$表示考慮了前$i$個字符,$S$集合的環已經被破壞,是否和原圖同樣爲$j$時邊權的最小值,DP便可。集合

由於最多隻有$13$個環,因此時間複雜度爲$O(n+26^2\times2^{13})$。di

 

#include<cstdio>
const int N=26,M=1000010,inf=~0U>>1;
int n,c,m,flag,i,j,k,S,U,v[N][N],ap[N],w[N][N],g[N],fa[N],d[N],vis[N],del[N],id[N];
int f[N+1][1<<(N/2)][2],ans;char a[M],b[M];
int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);}
inline void up(int&a,int b){if(a>b)a=b;}
int main(){
  scanf("%d%d%s%s",&n,&c,a,b);
  for(i=0;i<n;i++)v[a[i]-'a'][b[i]-'a']++,ap[a[i]-'a']++;
  for(i=0;i<N;i++)for(j=0;j<N;j++){
    w[i][j]=ap[i]-v[i][j];
    if(i!=j)w[i][j]+=c;
  }
  for(i=0;i<N;i++)fa[i]=i,id[i]=-1;
  for(i=0;i<N;i++){
    for(k=j=0;j<N;j++)if(w[i][j]<w[i][k])k=j;
    d[g[i]=k]++;
    if(F(i)!=F(k))fa[fa[i]]=fa[k];
  }
  for(i=0;i<N;i++)if(!del[F(i)]){
    del[fa[i]]=vis[i]=1;
    for(j=g[i];!vis[j];j=g[j])vis[j]=1;
    if(g[j]==j)continue;
    for(id[j]=m,k=g[j];k!=j;k=g[k])id[k]=m;
    m++;
  }
  if(!m){
    for(i=0;i<N;i++)ans+=w[i][g[i]];
    return printf("%d",ans),0;
  }
  for(flag=1,i=0;i<N;i++)if(d[i]!=1)flag=0;
  for(i=0;i<=N;i++)for(S=0;S<1<<m;S++)for(j=0;j<2;j++)f[i][S][j]=inf;
  f[0][0][0]=0;
  for(i=0;i<N;i++)for(S=0;S<1<<m;S++)for(j=0;j<2;j++)if(f[i][S][j]<inf)for(k=0;k<N;k++){
    U=S;
    if(~id[i]&&k!=g[i])U|=1<<id[i];
    if(~id[k]&&(k!=g[i]||id[i]!=id[k]))U|=1<<id[k];
    up(f[i+1][U][j||k!=g[i]],f[i][S][j]+w[i][k]);
  }
  for(ans=inf,S=0;S<1<<m;S++)for(j=flag;j<2;j++)if(f[N][S][j]<inf)up(ans,f[N][S][j]+(m-__builtin_popcount(S))*c);
  return printf("%d",ans),0;
}
相關文章
相關標籤/搜索