这题的状态设计非常巧妙,因为dp[S]表示的并非当前正确的值,而是维护一个中间量,这个中间量在到达末状态时才正确
当然官方的题解其实更加直观,只不过理解起来其实有点困难
/* 给定一个串s,字符集为20,求一个长为m的序列t,设pos[ch]为ch在t中的位置 确定一个t使得sum{ |pos[s[i]]-pos[s[i+1]]| } 先预处理cnt[][]数组用来存s中各种字符对的数量 然后进行状态压缩dp,从左到右按位确定t,S表示已经用掉的字符状态 用增量法对每种字符单独统计贡献,新增一个字符时,所有未在集合中的字符和已经在集合中的字符所形成的对数的贡献都会+1 dp[S]表示字符集是S时的贡献,那么对于每个S,都可以更新S|(1<<i)的状态 */ #include<bits/stdc++.h> using namespace std; #define N 200005 #define ll long long int n,m; char s[N]; ll dp[1<<21],cnt[21][21]; int main(){ //int t;cin>>t;while(t--){ scanf("%d%d",&n,&m); scanf("%s",s); int len=strlen(s); memset(cnt,0,sizeof cnt); for(int i=0;i<len-1;i++){ cnt[s[i]-'a'][s[i+1]-'a']++; } for(int S=0;S<(1<<m);S++)dp[S]=0x3f3f3f3f; dp[0]=0; for(int S=0;S<(1<<m);++S){ ll tot=0;//在状态S下新加入任意一个点 for(int i=0;i<m;i++)if(!(S>>i & 1)){ for(int j=0;j<m;j++)if(S>>j & 1) tot+=cnt[i][j]+cnt[j][i]; } for(int i=0;i<m;i++)if(!(S>>i & 1)) dp[S|(1<<i)]=min(dp[S|(1<<i)],dp[S]+tot); } cout<<dp[(1<<m)-1]<<' '; //} }