題意:長度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; }