邦邦的大合唱站队
题目背景
BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题。
题目描述
N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
输入格式
第一行2个整数N,M。
接下来N个行,每行一个整数 a i ( 1 ≤ a i ≤ M ) a_i(1\le a_i \le M) ai(1≤ai≤M),表示队列中第i个偶像的团队编号。
输出格式
一个整数,表示答案
样例 #1
样例输入 #1
12 4
1
3
2
4
2
1
2
3
1
1
3
4
样例输出 #1
7
提示
【样例解释】
1 3 √
3 3
2 3 √
4 4
2 4 √
1 2 √
2 2
3 2 √
1 1
1 1
3 1 √
4 1 √
【数据规模】
对于20%的数据, N ≤ 20 , M = 2 N\le 20, M=2 N≤20,M=2
对于40%的数据, N ≤ 100 , M ≤ 4 N\le 100, M\le 4 N≤100,M≤4
对于70%的数据, N ≤ 2000 , M ≤ 10 N\le 2000, M\le 10 N≤2000,M≤10
对于全部数据, 1 ≤ N ≤ 1 0 5 , M ≤ 20 1\le N\le 10^5, M\le 20 1≤N≤105,M≤20
思路+代码
由于 M M M 很小,也就是乐队数很少,因此我们可以考虑状态压缩,怎么压缩呢?就是乐队的排序的所有情况进行枚举即可。
状态方程: f [ i ] = m i n { f [ i x o r 2 j ] + n u m [ j ] − l e n t h [ x , y ] [ j ] } f[i]=min\{f[i \ xor \ 2^j]+num[j] -lenth[x,y][j]\} f[i]=min{f[i xor 2j]+num[j]−lenth[x,y][j]}
n u m [ j ] num[j] num[j]代表团队 j j j的总人数, l e n t h [ x , y ] lenth[x,y] lenth[x,y]代表在区间 [ x , y ] [x,y] [x,y]中团队 j j j的总人数。
那么代码就很好写了:
//枚举所有乐队的排列
//f[1101] 表示1/2/4占满前面
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1<<22,M = 1e5+10;
int f[N];
int n,k;
int s[M][21];
int w[M],num[M];//num 表示团队总人数
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>w[i];
for(int i=1;i<=n;i++){
num[w[i]]++;
for(int j=1;j<=k;j++)s[i][j]=s[i-1][j];
s[i][w[i]]++;
}
memset(f,0x3f,sizeof f);
f[0]=0;
for(int i=1;i<(1<<k);i++){
int length=0;
for(int j=1;j<=k;j++)if(i>>(j-1)&1)length+=num[j];
for(int j=1;j<=k;j++){
if(i>>(j-1)&1){
f[i]=min(f[i],f[i^(1<<(j-1))]+num[j]-s[length][j]+s[length-num[j]][j]);
}
}
}
cout<<f[(1<<k)-1];
return 0;
//kkk
}