luogu P3694 邦邦的大合唱站队 |状压DP
题目描述
N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
输入格式
第一行2个整数N,M。
接下来N个行,每行一个整数\(a_i(1\le a_i \le M)\),表示队列中第i个偶像的团队编号。
输出格式
一个整数,表示答案
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
int res=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
while(ch>='0'&&ch<='9'){res=(res<<3)+(res<<1)+ch-'0';ch=getchar();}
return res*f;
}
const int N=1e5+5,M=(1<<21);
int n,m;
int dp[M],sum[N][21];
signed main(){
n=read(),m=read();
for(int i=1,x;i<=n;i++){
x=read();
for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j];
sum[i][x]++;
}
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int S=0;S<(1<<m);S++){
int finish=0;
for(int j=0;j<m;j++)if((S>>j)&1)finish+=sum[n][j+1];
for(int j=0;j<m;j++){
if((S>>j)&1)continue;
int l=finish,r=l+sum[n][j+1];
dp[S|(1<<j)]=min(dp[S|(1<<j)],dp[S]+sum[n][j+1]-(sum[r][j+1]-sum[l][j+1]));
}
}
cout<<dp[(1<<m)-1]<<endl;
}
不以物喜,不以己悲