题解 P3694 【邦邦的大合唱站队】
题解开始前请允许我高呼一声:BangDream天下第一!
BangDream国服开始公测啦,有没有小伙伴一起来玩啊OwO~
回归正题。
首先注意到这是一道偶像多达五个数量级的LIVE题目,要么贪心要么DP,又注意到虽然N很大,但总乐队数仅有20,可考虑对其状压。
我们可以设状态S为当前已经排列好的乐队编号集合,那么先不管这几个乐队怎么排列,她们的人数总和是一定的(不妨设其为
那我们枚举站在最后一个位置的乐队(不妨设人数为
其中,
于是便得到了最终的转移方程,设
其中
然后就可以愉快的AC辣,上丑陋的代码:
#include <iostream>
#include <cstring>
using namespace std;
int n,m,x,f[2000000];
//首先枚举人肯定傻逼TLE,数据范围只能用状压
//设f[i]--> 状态I表示从左往右被标记的乐队已排好;
//f[i]表示状压为I时所出列最少人数。
int s[100010][30],num[30],sm[2000000];
bool d(int s,int x){
return (s&(1<<(x-1)));
}
void dfs(int x,int s,bool b)
{
if (x==m) return;
if (b) sm[s|(1<<x)]=sm[s]+num[x+1],dfs(x+1,s|(1<<x),1),dfs(x+1,s|(1<<x),0);
else dfs(x+1,s,1),dfs(x+1,s,0);
}
int main()
{
cin>>n>>m;
for (int i=1;i<=n;i++) {
cin>>x;
for (int j=1;j<=m;j++) s[i][j]=s[i-1][j];
s[i][x]++; num[x]++;
}
dfs(0,0,0); dfs(0,0,1);
memset(f,0x3f,sizeof(f)); f[0]=0;
for (int i=1;i<(1<<m);i++)
{
for (int j=1;j<=m;j++) if (d(i,j)) {
int l=sm[i^(1<<j-1)],r=sm[i];
f[i]=min(f[i],f[i^(1<<(j-1))]+(r-l)-(s[r][j]-s[l][j]));
}
}
cout<<f[(1<<m)-1]<<endl;
return 0;
}