P3694 邦邦的大合唱站队/签到题(状压dp)
P3694 邦邦的大合唱站队/签到题
题目背景
BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题。
题目描述
N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
输入输出格式
输入格式:
第一行2个整数N,M。
接下来N个行,每行一个整数a_i(1\le a_i \le M)ai(1≤ai≤M),表示队列中第i个偶像的团队编号。
输出格式:
一个整数,表示答案
输入输出样例
输入样例#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\le 20, M=2N≤20,M=2
对于40%的数据,N\le 100, M\le 4N≤100,M≤4
对于70%的数据,N\le 2000, M\le 10N≤2000,M≤10
/* 状压dp 状态:dp[i]表示i状态下最小的出列(不一致)的个数。 比如dp[1101]表示从头到位为1/3/4乐队的偶像的最小出列个数。 预处理sum[i][j]表示前i个人中j种的数量 dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j]))); */ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define inf 100000000 #define N 100007 using namespace std; int n,m; int a[N],dp[(1<<21)+1],sum[N][21]; int main() { scanf("%d%d",&n,&m); for(int i=1; i<=n; i++) { scanf("%d",&a[i]); a[i]--; for(int j=0; j<m; j++) { sum[i][j]=sum[i-1][j]; if(j==a[i]) sum[i][j]++; } } for(int i=0; i<(1<<m); i++) dp[i]=inf; dp[0]=0; for(int i=0; i<(1<<m); i++) { int Sum=0; for(int j=0; j<m; j++) if((1<<j)&i) Sum+=sum[n][j]; for(int j=0; j<m; j++) { if((1<<j)&i) continue; int num=sum[n][j]; int r=Sum+num; int l=Sum; dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j]))); } } printf("%d\n",dp[(1<<m)-1]); return 0; }
对于全部数据,1\le N\le 10^5, M\le 201≤N≤105,M≤20
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。