题意:给你一个长度为n的序列 问你需要多少次两两交换 可以让相同的数字在一个区间段
思路:我们可以预处理一个数组cnt[i][j]表示把i放到j前面需要交换多少次 然后二进制枚举后 每次选择一个为1的位置 考虑这个位置最后加进来的花费取最小
#include <bits/stdc++.h> using namespace std; const int inf = 0x3f3f3f3f; const double eps = 1e-6; const int N = 4e5+7; typedef long long ll; const ll mod = 1e9+7; using namespace std; int a[N]; ll cnt[30][30]; vector<int> v[30]; ll dp[1<<20]; void work(){ for(int i=1;i<=20;i++) for(int j=1;j<=20;j++){ if(i==j) continue; if(v[j].size()==0||v[i].size()==0){ continue; } int po1,po2; po1=po2=0; for(po1=0;po1<v[i].size();po1++){ // cout<<v[i].size()<<endl; while(1){ // cout<<po1<<" "<<po2<<endl; if(po2==v[j].size()-1||v[j][po2+1]>v[i][po1]) break; po2++; // cout<<"1"<<endl; } if(v[i][po1]>v[j][po2]){ cnt[i][j]+=(po2+1); // cout<<po2+1<<" "; } // cout<<endl; } // cout<<cnt[i][j]<<" "<<i<<" "<<j<<endl; } } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i],v[a[i]].push_back(i); work(); for(int i=0;i<(1<<20);i++) dp[i]=0x3f3f3f3f3f3f3f3f; dp[0]=0; for(int i=1;i<(1<<20);i++){ for(int j=0;j<20;j++){ if(i&(1<<j)){ ll sum=0; for(int k=0;k<20;k++){ if(k==j) continue; if(i&(1<<k)) sum+=cnt[k+1][j+1]; } dp[i]=min(dp[i],dp[i^(1<<j)]+sum); } } } cout<<dp[(1<<20)-1]<<endl; return 0; }