题目
感谢洛谷第二篇题解
其实看这道题实际上就是枚举m个乐队的排列,然后对应的站在该乐队多占的位置,却不是该乐队的人需要出队。然后最后一定可站成同一团队连续的排列。那么下面的dp方程就很好理解啦。
注意: 从状态S 在S后面放一个乐队j 一定需要增加的出队的人数为 乐队j所占的容量num[j]处不是乐队j的人的个数。
//dp[d]=dp[S]+cnt[j]-(s[r][j]-s[l][j]); r=len+cnt[j],l=len;
//dp[S]:状态为S(已经排列好了的乐队可以用状态S表示)
//cnt[j]:第j个乐队的人数
//num[i][j]:刚开始时(即输入) 前i个人是第j个乐队的人数
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5,M=21,INF=0x3f3f3f3f;
int cnt[N],num[N][M],dp[1<<M];
int main(){
int n,m,t;scanf("%d%d",&n,&m),t=1<<m;
for(int i=1,x;i<=n;++i) scanf("%d",&x),++cnt[x],num[i][x]=1;
for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) num[i][j]+=num[i-1][j];
memset(dp,INF,sizeof dp),dp[0]=0;
for(int i=0;i<t;++i){
int len=0;
for(int j=1;j<=m;++j) if(i&(1<<(j-1))) len+=cnt[j];
for(int j=1;j<=m;++j){
if(i&(1<<(j-1))) continue;
int d=(i|(1<<(j-1)));
dp[d]=min(dp[d],dp[i]+cnt[j]-(num[len+cnt[j]][j]-num[len][j]));
}
}
printf("%d\n",dp[t-1]);
}