状压dp
pref[i][j]代表前i个元素中j颜色的总数
cnt[i]代表i颜色的总数
cond[i] 表示情况i时排列完成时有序元素的数量
如果要把l,r范围内的人变为同一个乐队j的,至少需要让(r-l)-(pref[r][j]-pref[l][j])个人移动位置(可以把r-l认为是要整理成相同颜色的元素数量,后者是可以不做处理的元素数量)
可以得到递推式:
当i在第j位为1时:dp[i]=min(dp[i],dp[i^(1<<(j-1))]+r-l-pref[r][j]+pref[l][j])
//cyc
#pragma GCC optimize("Ofast")
#pragma GCC target("avx,avx2,fma")
#pragma GCC optimization ("unroll-loops")
#include<bits/stdc++.h>
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define mst(a) memset(a,0,sizeof a)
using namespace std;
typedef pair<int,int> pii;
const int maxn=(1<<20)+5;
const int maxl=1e5+5;
int n,m;
int dp[maxn+5];
int pref[maxl][25];
int cnt[25];
int cond[maxn+5];
bool pd(int s,int x)
{
return (s&(1<<x));
}
void dfs(int n1,int x,int b)//n1 当前状态 x 目前运算位数 b是否在这位加上1
{
if(x==m){
return;
}
if(b){
cond[n1|(1<<x)]=cond[n1]+cnt[x+1];//
dfs(n1|(1<<x),x+1,1);
dfs(n1|(1<<x),x+1,0);
}
else{
dfs(n1,x+1,1);
dfs(n1,x+1,0);
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
int p;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
pref[i][j]=pref[i-1][j];
}
cin>>p;
pref[i][p]++;
cnt[p]++;
}
dfs(0,0,1);
dfs(0,0,0);
memset(dp,0x3f3f3f3f,sizeof dp);
dp[0]=0;
for(int i=1;i<(1<<m);i++){
for(int j=1;j<=m;j++){
if(pd(i,j-1)){
int l,r;
r=cond[i];
l=cond[i^(1<<(j-1))];
dp[i]=min(dp[i],dp[i^(1<<(j-1))]+r-l-pref[r][j]+pref[l][j]);
}
}
}
cout<<dp[(1<<m)-1]<<endl;
}