题意:
给出n个有序排列的弹珠$(n\leq 4e5)$,每个弹珠有一种颜色(不超过20种颜色),现执行操作,每次操作可以选任意一对相邻的弹珠,将其交换。要求:最终对任意颜色的弹珠,能找到l,r使$[l,r]$内所有弹珠都是该颜色并且所有该颜色弹珠都在该区间内,(即所有相同颜色的弹珠都在一起),求最少操作次数。
解题思路:
颜色只有20种,有点明显的状压,但是要先进行预处理操作,设cost[i][j]为把i放到j后面,i与j所交换的次数首先预处理出20种颜色互相交换的cost[i][j]。
接下来,采用状压。位运算中,每位为1表示该颜色已经加入,为0表示没有加入,用dp[i]表示在i状态下的最少的交换次数,假设在i状态下第j种颜色已经被确定,即$((1<<j)&i)$不为0,$dp[i]=max(dp[i],\sum_{k=1}^{20}cost[j][k])$,其中$(1<<k&i)=0$即第k种颜色已经确定。
答案即为dp(1<<20-1),复杂度$O(2^{n})$
最后贴上代码。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef pair <int,int> pii; #define rep(i,x,y) for(int i=x;i<y;i++) #define rept(i,x,y) for(int i=x;i<=y;i++) #define per(i,x,y) for(int i=x;i>=y;i--) #define pb push_back #define mp make_pair #define fi first #define se second #define de(x) cout<< #x<<" = "<<x<<endl #define dd(x) cout<< #x<<" = "<<x<<" " #define mes(a,b) memset(a,b,sizeof a) const ll inf=1e18; const int maxn=25; vector <int> v[maxn]; ll cost[maxn][maxn]; ll dp[1<<20]; int main() { ios::sync_with_stdio(false); cin.tie(0); int n; cin>>n; mes(cost,0); rep(i,0,n) { int x; cin>>x; v[x].pb(i); } rept(i,1,20)//i is in front of j { rept(j,1,20) { if(i==j) continue; int p=-1; rep(k,0,v[i].size()) { while(p+1<v[j].size()&&v[j][p+1]<v[i][k]) p++; cost[i][j]+=p+1; } } } dp[0]=0; rep(i,1,(1<<20)) { dp[i]=inf; rep(j,0,20) { if(i&(1<<j)) { int k=i-(1<<j); long long plus=0; rep(l,0,20) { if((1<<l)&k) plus+=cost[j+1][l+1]; } dp[i]=min(dp[i],plus+dp[k]); } } } cout<<dp[(1<<20)-1]<<"\n"; return 0; }