題目鏈接https://codeforces.com/contest/1238/problem/E
題意
給出一個由m個小寫字母組成的長度爲n的字符串(m<=20,n<=1e5),另外對這m個字符構造出一個序列,序列內兩兩字符的座標差值是兩個字符的距離,問從左到右走字符串的每一個字符,距離最小是多少。
題解
狀壓代表已經確定了狀態爲的字符放在最左邊的情況,狀態轉移方程可以表示爲:
if(s&(1<<i)) dp[s]=min(dp[s],dp[s^(1<<i)]+tmp);
爲在當前狀態內的字符與不在內的字符在原串相鄰的對數,代表這個字符放在所有已枚舉的字符的最右邊。這樣子的可以不用管後面的情況,也能計算出貢獻,好酷的dp…
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+7;
const int inf=1e9;
int n,m;
int mp[30][30];
int dp[N];
char s[N];
int main(){
scanf("%d%d",&n,&m);
scanf("%s",s);
int len=strlen(s);
for(int i=0;i<len-1;i++){
int u=s[i]-'a',v=s[i+1]-'a';
mp[u][v]++;mp[v][u]++;
}
dp[0]=0;
for(int s=1;s<(1<<m);s++){
dp[s]=inf;
int tmp=0;
for(int i=0;i<m;i++){
if(!(s&(1<<i))) continue;
for(int j=0;j<m;j++){
if((s&(1<<j))) continue;
tmp+=mp[i][j];
}
}
for(int i=0;i<m;i++){
if(s&(1<<i)) dp[s]=min(dp[s],dp[s^(1<<i)]+tmp);
}
}
printf("%d\n",dp[(1<<m)-1]);
return 0;
}