洛谷P3694 邦邦的大合唱站队/签到题
Posted Soda
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了洛谷P3694 邦邦的大合唱站队/签到题相关的知识,希望对你有一定的参考价值。
题目背景
BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题。
题目描述
N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
输入输出格式
输入格式:
第一行2个整数N,M。
接下来N个行,每行一个整数a_i(1\le a_i \le M)ai?(1≤ai?≤M),表示队列中第i个偶像的团队编号。
输出格式:
一个整数,表示答案
输入输出样例
说明
【样例解释】
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
对于全部数据,1\le N\le 10^5, M\le 201≤N≤105,M≤20
#include<cstdio> const int N=100005,M=26; int a[N],b[M],n,m,s[M],sum[M][N],ans=1<<30; bool vis[M]; void dfs(int i, int tot){ if(i>n){ if(tot<ans) ans=tot; return; } if(tot>ans) return; for(int j=1;j<=m;j++) if(!vis[j]){ vis[j]=true; dfs(i+s[j],tot+s[j]-sum[j][i+s[j]-1]+sum[j][i-1]); vis[j]=false; } } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); s[a[i]]++; } for(int i=1;i<=m;i++) for(int j=1;j<=n;j++){ sum[i][j]=sum[i][j-1]; if(a[j]==i) sum[i][j]++; } dfs(1,0); printf("%d",ans); }
/* 状态压缩,dp[i]表示达到i状态出队的最小人数,sum[i][j]表示前i个人有几个属于j乐队那么枚举一个l,r则有dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+(r-l-(sum[r][j]-sum[l][j]))); */ #include<iostream> #include<cstdio> using namespace std; int n,m,dp[(1<<20)+1],a[100010],sum[100010][21]; int main(){ freopen("Cola.txt","r",stdin); 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(a[i]==j)sum[i][j]++; } } for(int i=0;i<(1<<m);i++)dp[i]=0x7fffffff;dp[0]=0; for(int i=0;i<(1<<m);i++){ int s=0; for(int j=0;j<m;j++)if(i&(1<<j))s+=sum[n][j]; for(int j=0;j<m;j++){ if(i&(1<<j))continue; int num=sum[n][j]; int r=s+num,l=s; dp[i|(1<<j)]=min(dp[i|(1<<j)],(r-l-(sum[r][j]-sum[l][j])+dp[i])); } } printf("%d",dp[(1<<m)-1]); }
以上是关于洛谷P3694 邦邦的大合唱站队/签到题的主要内容,如果未能解决你的问题,请参考以下文章
Luogu P3694 邦邦的大合唱站队 状压dpBy cellur925