题意:长度4e5的数字序列,不一样数字个数至多20个,每次操做可选取两个相邻数字并交换位置,如今想要使序列中全部相同数字都排列在一块儿,问至少须要几回操做。时限4s.c++
题解:由不一样数字个数至多20应该想到状压DP......spa
如今假想所有排完以后的状态,根据不一样数字块的位置显然有\(20!\)种状况,那么咱们能够假想数字1到20是一个一个完成合并并放到数列左端的。这样的话\(DP[i]\)就表示\(i\)数位上为\(1\)的数字完成合并并丢在左边所需操做次数。code
预处理一个\(val[i][j]\)表示把全部数字\(i\)放到全部数字\(j\)左边所需操做次数,那么很显然转移的增量就是这个数字与其余全部未完成合并的数字的\(val\)和。(好巧妙TuT)get
复杂度:o(\(2^{20}*400\)).it
#include <bits/stdc++.h> using namespace std; int n; vector<int> pos[20]; typedef long long ll; ll dp[1<<20],val[20][20]; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ int x; scanf("%d",&x); pos[x-1].push_back(i); } for(int i=0;i<20;i++){ for(int j=0;j<20;j++){ if(i==j||pos[j].empty()) continue; for(int k=0;k<int(pos[i].size());k++){ int x=pos[i][k]; val[i][j]+=lower_bound(pos[j].begin(),pos[j].end(),x)-pos[j].begin(); } } } memset(dp,0x3f3f3f3f,sizeof(dp)); dp[0]=0; for(int i=0;i<(1<<20);i++){ vector<int> u; for(int j=0;j<20;j++){ if((1<<j)&i) continue; u.push_back(j); } for(int j=0;j<int(u.size());j++){ int x=u[j]; ll sum=0; for(int k=0;k<int(u.size());k++){ if(u[k]!=x) sum+=val[x][u[k]]; } dp[i|(1<<x)]=min(dp[i|(1<<x)],dp[i]+sum); } } cout << dp[(1<<20)-1]; return 0; }