状压dp

应用思想,找准状态,多考虑状态和\(f\)答案数组的维数(这个题主要就是找出来状态如何转移)

题目背景

\(BanG Dream!\)里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题。

题目描述

\(N\)个偶像排成一列,他们来自\(M\)个不同的乐队。每个团队至少有一个偶像。

现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。

请问最少让多少偶像出列?

输入格式

第一行\(2\)个整数\(N\),\(M\)。

接下来\(N\)个行,每行一个整数\(a_i(1\le a_i \le M)\),表示队列中第i个偶像的团队编号。

输出格式

一个整数,表示答案

输入输出样例

输入

12 4

1

3

2

4

2

1

2

3

1

1

3

4

输出

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=2\)

对于\(40\%\)的数据,\(N\le 100, M\le 4\)

对于\(70\%\)的数据,\(N\le 2000, M\le 10\)

对于全部数据,\(1\le N\le 10^5\), \(M\le 20\)

分析

看到这友好的乐队数范围,很容易就想到了状压dp,但是状态到底找哪个,记录答案的\(f\)数组开几维都是问题,我们来分析一下,题目中给出的乐队\(M\)的范围是\(20\),而状态压缩就是从小的范围入手的,所以\(f\)数组的状态那一维肯定是关于乐队的,再看题目中问的,询问的是要最少拿出来多少人,那么这个状态肯定就是第几个乐队入队的状态,记录的是当前状态下出队人数的最小,然后枚举最后一个位置的乐队,那么需不需要第二维呢?看起来是不需要的,因为我们每次转移都是从上一次当前乐队的人未放入到放入,然后加上当前乐队人数,减去增加的长度中当前乐队的人数,也就是算出前边需要出队的人数(这个人数用\(sum\)数组记录前缀和来实现),就是这一次需要拿出来的人数,然后每次转移都取一次\(min\),最终状态全为\(1\)的时候的\(f\)数组就是答案。状态转移方程如下:

\[f[i] = min(f[i \ xor \ (1<<(j-1) ] + num[j] - (sum[len][j]-sum[len-num[j]][j]),f[i])
\]

其中\(num\)是第\(j\)个乐队的人数,\(len\)是到现在状态的队伍长度,预处理一下就可以。\(sum\)就是当前乐队在这一段中的人数。

如果一共有\(M\)个乐队,最终答案就是\(f[(1<<M)-1]\)

总结一下数组代表的东西:

\(f[i]\)代表状态为\(i\)时出队的最小人数,\(sum[i][j]\)表示前\(i\)长度里,\(j\)乐队的人数,\(num[j]\)代表的就是\(j\)乐队的总人数。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 21;
int f[1<<maxn];
int num[maxn];
int a[100005];
int sum[100005][maxn];
int n,m;
int main(){
cin>>n>>m;
for(int i=1;i<=n;++i){
cin>>a[i];
}
for(int i=1;i<=n;++i){
num[a[i]]++;//记录每个乐队的总人数
for(int j=1;j<=m;++j)sum[i][j] = sum[i-1][j];//初始化sum数组
sum[i][a[i]]++;//求每个乐队人数的前缀和
}
memset(f,0x3f,sizeof(f));//初始化最大值
f[0] = 0;//一个乐队都没有的时候取0人
for(int i=1;i<(1<<m);++i){
int len = 0;
for(int j=1;j<=m;++j)if(i & (1<<(j-1)))len += num[j];//如果当前状态下取了j乐队的人,总人数就加上j乐队的人数
for(int j=1;j<=m;++j){//枚举站在最后一个位置的乐队
if(i & (1<<(j-1)))//效率优化,当前状态取了他再进行取min,不然取min没有意义
f[i] = min(f[i],f[i ^ (1<<(j-1))] + num[j] - sum[len][j]+sum[len - num[j]][j]);//状态转移,取第j个乐队要加上该乐队人数,减去这一段中本来就有的该乐队人数
}
}
int ans = f[(1<<m)-1];
cout<<ans;
}

洛谷P3694 邦邦的大合唱站队【状压dp】的更多相关文章

  1. P3694 邦邦的大合唱站队 (状压DP)

    题目背景 BanG Dream!里的所有偶像乐队要一起大合唱,不过在排队上出了一些问题. 题目描述 N个偶像排成一列,他们来自M个不同的乐队.每个团队至少有一个偶像. 现在要求重新安排队列,使来自同一 ...

  2. 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)

    洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...

  3. 洛谷 P3694 邦邦的大合唱站队 状压DP

    题目描述 输入输出样例 输入 #1 复制 12 4 1 3 2 4 2 1 2 3 1 1 3 4 输出 #1 复制 7 说明/提示 分析 首先要注意合唱队排好队之后不一定是按\(1.2.3..... ...

  4. 洛谷P1896 [SCOI2005]互不侵犯King【状压DP】

    题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入格式: 只有一行,包含两个数N,K ...

  5. 【洛谷 P1896】[SCOI2005]互不侵犯(状压dp)

    题目链接 题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 这是道状压\(DP\)好题啊.. ...

  6. BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】

    [题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...

  7. 【洛谷5492】[PKUWC2018] 随机算法(状压DP)

    点此看题面 大致题意: 用随机算法求一张图的最大独立集:每次随机一个排列,从前到后枚举排列中的点,如果当前点加入点集中依然是独立集,就将当前点加入点集中,最终得到的点集就是最大独立集.求这个随机算法的 ...

  8. 洛谷P2396 yyy loves Maths VII【状压dp】

    题目:https://www.luogu.org/problemnew/show/P2396 题意:有n个数,每次选择一个表示走$a[i]$步,每个数只能选一次. 最多有两个厄运数字,如果走到了厄运数 ...

  9. 洛谷 3112 [USACO14DEC]后卫马克Guard Mark——状压dp

    题目:https://www.luogu.org/problemnew/show/P3112 状压dp.发现只需要记录当前状态的牛中剩余承重最小的值. #include<iostream> ...

  10. 洛谷 P7324 - [WC2021] 表达式求值(状压+dp)

    题面传送门 现场人傻系列-- 首先建出 \(E\) 的表达式树,具体来说表达式的每一个叶子节点表示一个数组 \(A_i\),每一个非叶子节点都表示一次运算,它的值表示左右儿子进行该运算后得到的结果.这 ...

随机推荐

  1. Java实现 LeetCode 753 破解保险箱(递归)

    753. 破解保险箱 有一个需要密码才能打开的保险箱.密码是 n 位数, 密码的每一位是 k 位序列 0, 1, -, k-1 中的一个 . 你可以随意输入密码,保险箱会自动记住最后 n 位输入,如果 ...

  2. Java实现 LeetCode 605 种花问题(边界问题)

    605. 种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给定一个花坛(表示为一个数组包含0和1,其中0表示没种 ...

  3. Java实现 LeetCode 329 矩阵中的最长递增路径

    329. 矩阵中的最长递增路径 给定一个整数矩阵,找出最长递增路径的长度. 对于每个单元格,你可以往上,下,左,右四个方向移动. 你不能在对角线方向上移动或移动到边界外(即不允许环绕). 示例 1: ...

  4. Java实现第九届蓝桥杯小朋友崇拜圈

    小朋友崇拜圈 题目描述 班里N个小朋友,每个人都有自己最崇拜的一个小朋友(也可以是自己). 在一个游戏中,需要小朋友坐一个圈, 每个小朋友都有自己最崇拜的小朋友在他的右手边. 求满足条件的圈最大多少人 ...

  5. java实现第五届蓝桥杯六角幻方

    六角幻方 里面的*在编写的时候会自动编译成线,这里就用代码的格式把题目弄过来 把 1 2 3 ... 19 共19个整数排列成六角形状,如下: * * * * * * * * * * * * * * ...

  6. 【整理】JVM知识点大梳理

    JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.引入Java语 ...

  7. leetcode之两数相加解题思路

    问题描述 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使 ...

  8. 仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'xxxx'中的标识列指定显式值

    执行以下sql INSERT INTO [Country] VALUES (, N'中国', N'China', N'CN'); 提示错误 仅当使用了列列表并且 IDENTITY_INSERT 为 O ...

  9. MySql多表查询优化

    一.多表查询连接的选择 相信内连接,左连接什么的大家都比较熟悉了,当然还有左外连接什么的,基本用不上,我就补贴出来了,这个图只是让大家熟悉一下各种连接查询.然后要告诉大家的是,需要根据查询的信息,想好 ...

  10. Servlet Session MVC模式

    一   什么是Session 当首次使用session时,服务器端要创建session,session是保存在服务器端,而给客户端的session的id(一个cookie中保存了sessionId). ...