題意
一個只包含前個字母的長度爲的字符串
對於一個包含前個字母的排列,設表示字母在排列中的位置
求一個排列使得最小,輸出最小值即可
題解
設表示字母有多少次相鄰(特別的)
那麼就是最小化
注意到只有,可以考慮狀壓,設全集爲
對於一個集合,假設新加入一個字母,我們就把放在排列的位(由於是絕對值的差,全員的位置都不影響結果)
可以證明這樣做是能取到所有最優情況的,因爲擴展到集合時,中每個字母放在最後的情況都會被算到
那麼把加入集合的代價爲
注意此時是無法暴力存下位置然後枚舉轉移的,因爲可能會有很多總代價相同的排列,時間複雜度可以達到
設,這個是可以預處理出來的
主要考慮後面減去的如何轉化
而由於我們無法存下位置,所以只能在上面想如何處理
假設再新加入一個字母,則其代價中與有關的就只有這一項
而此時,其中
所以對於所有的,的貢獻和就爲
所以就可以等價轉化爲
於是設表示集合的等價代價的最小和,那麼
時間複雜度
#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int N=1e5+5,M=25,S=(1<<21)+5;
typedef long long ll;
int n,m,T,Mi[M],cnt[M][M];
int f[S],Log[S],cntBit[S],sumC[S][M];
char s[N];
int main(){
#ifndef ONLINE_JUDGE
file("s");
#endif
scanf("%d %d\n",&n,&m);
gets(s+1);T=(1<<m)-1;
fp(i,2,n)
++cnt[s[i]-'a'][s[i-1]-'a'],
++cnt[s[i-1]-'a'][s[i]-'a'];
fp(i,0,m)cnt[i][i]=0;
Mi[0]=1;Log[0]=-1;
fp(i,1,m-1)Mi[i]=Mi[i-1]<<1;
fp(s,1,T){
Log[s]=Log[s>>1]+1;
cntBit[s]=cntBit[s>>1]+(s&1);
int y=Log[s&(-s)];
fp(x,0,m-1)sumC[s][x]=sumC[s^Mi[y]][x]+cnt[x][y];
}
memset(f,127,sizeof f);f[0]=0;
fp(s,0,T)fp(i,0,m-1)if(!(s&Mi[i]))
cmin(f[s|Mi[i]],f[s]+cntBit[s]*(sumC[s][i]-sumC[T^s][i]));
printf("%d\n",f[T]);
return 0;
}