题意:

  给出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;
}