【题解】P3694 邦邦的大合唱站队
【题解】P3694 邦邦的大合唱站队
题目传送门
题目描述
N个偶像排成一列,他们来自M个不同的乐队。每个团队至少有一个偶像。
现在要求重新安排队列,使来自同一乐队的偶像连续的站在一起。重新安排的办法是,让若干偶像出列(剩下的偶像不动),然后让出列的偶像一个个归队到原来的空位,归队的位置任意。
请问最少让多少偶像出列?
Solution
首先,想象最终队列的排列方式,其最多只有M!种
如111222233,或222233111,331112222……
于是我们可以用每一种排列方式分别与原数列的每一个位置依次进行比对,
显然,若原数列的相同位置上的数与最终数列上的数相同,则其一定不用出列
否则,则一定要出列
O(N)统计个数即可,总复杂度O(NM!)
考虑优化统计的复杂度
可以用前缀和sum[i][j]记录原数列前i个位置,共有多少个偶像来自团队j
再用一个num[i]表示团队i的总个数
这样每次统计O(M),总复杂度O(M*M!)
如何用状压dp呢
用s表示,当前哪些队伍已被排完。第i位是1,就表示第i个团队被排完
用f[s]表示前count(s)个队伍有哪些,排好他们的最少花费
转移时,枚举每一个可能的结尾队伍,按照刚才的方法算出花费,记录最小值
复杂度是(m*2^m)
那么为什么复杂度降低了呢
回忆我们一开始的全排列解法
比如1234和2134两个排列,他们的前两个数是相同的,只是顺序不一样,而在全排列解法中,我们却重复算了两遍
但是!!!实际上,在转移时我们根本不关心他前面具体的顺序,我们只需要知道他已经排了哪几个团队,以及这些团队的占用的总长度,而后者可以用前者计算
dp之所以比暴力快,就在于它及时统计状态信息,避免重复运算
所以我们可以想到以当前排好了哪几个队伍为状态,那么也很自然的就运用到了状态压缩
总结
- 有些题没有思路,可以采取想象最终答案,并与原数据进行比对的策略
- 在从暴力优化到正解的过程中,可以考虑哪些信息被重复统计,是否状态冗余,然后想办法压缩状态,或记录信息
Code
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
register int x=0,w=1;
register char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') {ch=getchar();w=-1;}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar(); }
return x*w;
}
const int M=1e5+10;
int n,m,f[1<<20],sum[M][21],tot[21],len[1<<20];
int main()
{
n=read();m=read();
int x;
for(int i=1;i<=n;++i)
{
x=read();
for(int j=1;j<=m;++j) sum[i][j]=sum[i-1][j];
sum[i][x]++;
tot[x]++;
}
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<(1<<m);++i)
{
for(int j=0;j<m;++j)
{
if(i>>j&1) len[i]+=tot[j+1];
}
}
for(int i=1;i<(1<<m);++i)
{
for(int j=0;j<m;++j)
{
if(i>>j&1) f[i]=min(f[i],f[i^(1<<j)]+tot[j+1]-(sum[len[i]][j+1]-sum[len[i^(1<<j)]][j+1]) );
}
}
cout<<f[(1<<m)-1];
return 0;
}
原文地址:https://www.cnblogs.com/glq-Blog/p/15009567.html
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python贪吃蛇小游戏_完整源码免费分享
- GitHub修改昵称和用户名(图解详细教程)
- Python飞机大战小游戏_完整源码免费分享
- Linux求助命令
- Linux关机命令及步骤
- Java实现二叉树层次遍历:从上往下打印出二叉树的每个节点,同层节点从左至右打印。
- Django 用户认证系统使用总结
- 前端综合面试题(9道)
- 用SQL查询Oracle数据库名和实例名
- Hadoop历史服务器配置详细步骤
- MySQL常见关键字优先级
- Linux进程管理命令及状态详解
- sqoop把hive数据导入mysql出现中文乱码
- Flink实现WordCount(实操详细步骤)
- 在客户端创建要素图层 (FeatureLayer)