bzoj2064[和谐社会模拟赛]分裂
题意:给定一个初始集合和目标集合,有两种操作:1.合并集合中的两个元素,新元素为两个元素之和 2.分裂集合中的一个元素,得到的两个新元素之和等于原先的元素。要求用最小步数使初始集合变为目标集合,求最小步数。
记集合S的元素之和为sum(S)
首先,如果初始集合的子集S1和目标集合的子集S2满足sum(S1)==sum(S2),那么我们就可以把S1合并为一个元素再拆分为S2,然后再处理两个集合剩下的部分。
我们得到了一个很显然的状态转移方程。记f[S1][S2]为使用初始集合的S1子集得到目标集合的S2子集的最小花费(如果状态合法,必须满足sum(S1)==sum(S2),但并不一定是将S1全部合并为一个元素再拆成S2,因为S1的某个子集还可以与S2的某个子集的和相等,从而用S1的几个子集分别得到S2的几个子集)。转移的时候,如果直接暴力枚举S1和S2的每一个子集判断能否匹配,复杂度会达到(2^n)^4,30分。
这时有一个显然的优化,就是只考虑合法的状态,对于非法的状态(S1的和不等于S2的状态)不进行求解。我的做法是预处理出所有合法状态,然后仅在合法状态之间进行转移,复杂度为O(m^2),m为合法状态数。如果出题人愿意造数据的话,这个做法是可以卡掉的,比如初始状态和末状态都是10个1,那么总的合法状态数为C(10,1)^2+C(10,2)^2+C(10,3)^2+….+C(10,10)^2,达到了184755,肯定会T。但是我在bzoj上assert了一发,发现最多的合法状态数大于5000小于8000,那么平方的复杂度就过掉了。
不会被卡的做法是:我们转移的方式是将初始集合分成几个子集对应到目标集合的几个子集。假如我们将初始集合分成了x个子集,那么最后的花费就是初始集合元素个数+目标集合元素个数-2*x(举几个例子就能发现这个规律)。由于元素个数已经确定,我们的最优化目标转化为将初始集合分成尽量多的子集与目标集合的同样数目的子集一一对应(元素之和相等)。
记f[S1][S2]为从初始集合的S1子集和目标集合的S2子集中选取元素,最多能组成几组元素和相等的集合。(不一定使用S1和S2中的所有元素)。
转移时,我们分两种情况讨论,一种是sum(S1)!=sum(S2),这时一定无法用上所有的元素,那么只要枚举没有使用的一个元素S1中的i或S2中的j,f[S1][S2]=max(f[S1^i][S2],f[S1][S2^j])
一种是sum(S1)==sum(S2),这时一定可以用上所有的元素,那么我们是否需要枚举S1,S2的所有子集?看似只能通过这个方法使得状态从前面转移过来,那么复杂度上界又变成了(2^n)^4,30分。不过,再仔细想想。如果我们在最优解中拿掉一个元素,那么能够配对的集合对数一定会减1.反过来,f[S1][S2]的最优解一定可以通过一个在S1或S2中拿掉一个元素的状态增加一个配对的集合得到。(由于sum(S1)==sum(S2),而且任何一个状态中已经配对的元素都满足两边总和相等,所以拿掉一个元素后的状态在增添一个元素后一定可以增加一组配对的集合。)这个用枚举元素代替枚举子集的思路很巧妙。
仍然枚举S1,S2中的元素i,j,那么f[S1][S2]= max(f[S1^i][S2],f[S1][S2^j])+1。
#include<cstdio>
#include<cstring>
#include<cassert>
const int inf=0x3f3f3f3f;
inline int lowbit(int x){
return x&(-x);
}
inline int min(int a,int b){
return a<b?a:b;
}
int a[],b[];
int suma[],sumb[];
int f[][];
int g[];
const int maxn=;
int u[maxn],v[maxn];
int main(){
int n,m;
scanf("%d",&n);
for(int i=;i<n;++i){
scanf("%d",a+i);
suma[<<i]=a[i];
}
scanf("%d",&m);
for(int i=;i<m;++i){
scanf("%d",b+i);
sumb[<<i]=b[i];
}
int lim1=<<n,lim2=<<m;
for(int i=;i<lim1;++i){
suma[i]=suma[i^lowbit(i)]+suma[lowbit(i)];
}
for(int i=;i<lim2;++i){
sumb[i]=sumb[i^lowbit(i)]+sumb[lowbit(i)];
}
for(int i=;i<;++i)g[i]=g[i>>]+(i&);
int cnt=;
memset(f,0x3f,sizeof(f));
for(int i=;i<lim1;++i){
for(int j=;j<lim2;++j){
if(suma[i]==sumb[j]){
u[++cnt]=i;
v[cnt]=j;
f[i][j]=g[i]+g[j]-;
}
}
}
for(int i=;i<=cnt;++i){
for(int j=i+;j<=cnt;++j){
if((u[i]&u[j])||(v[i]&v[j])){
continue;
}
f[u[i]|u[j]][v[i]|v[j]]=min(f[u[i]|u[j]][v[i]|v[j]],f[u[i]][v[i]]+f[u[j]][v[j]]);
}
}
assert(cnt<=);
printf("%d\n",f[lim1-][lim2-]);
return ;
}
#include<cstdio>
#include<cstring>
const int inf=0x3f3f3f3f;
inline int lowbit(int x){
return x&(-x);
}
inline int max(int a,int b){
return a>b?a:b;
}
int a[],b[];
int suma[],sumb[];
bool e[][];
int f[][];
int g[];
int main(){
int n,m;
scanf("%d",&n);
for(int i=;i<n;++i){
scanf("%d",a+i);
suma[<<i]=a[i];
}
scanf("%d",&m);
for(int i=;i<m;++i){
scanf("%d",b+i);
sumb[<<i]=b[i];
}
int lim1=<<n,lim2=<<m;
for(int i=;i<lim1;++i){
suma[i]=suma[i^lowbit(i)]+suma[lowbit(i)];
}
for(int i=;i<lim2;++i){
sumb[i]=sumb[i^lowbit(i)]+sumb[lowbit(i)];
}
for(int i=;i<lim1;++i){
for(int j=;j<lim2;++j){
for(int k=;k<n;++k){
if(i&(<<k))f[i][j]=max(f[i][j],f[i^(<<k)][j]);
}
for(int k=;k<m;++k){
if(j&(<<k))f[i][j]=max(f[i][j],f[i][j^(<<k)]);
}
if(suma[i]==sumb[j])f[i][j]++;
}
}
printf("%d\n",n+m-*f[lim1-][lim2-]);
return ;
}
bzoj2064[和谐社会模拟赛]分裂的更多相关文章
- 2014-11-2 NOIP模拟赛1
Noip2009 团结模拟赛如题目理解困难,请自行阅读或参考样例.内存限制均为 256MB,时间限制均为 1s.出题人不会 故意 在题目中设置陷阱,但请自己注意程序的正确性.IO 文件名(.in/.o ...
- NOIP一系列模拟赛小结
NOIP越发接近了,于是自己也跟着机房的几位师兄一起做了几次NOIP模拟赛,收获颇多. #1-T1:求点集中的点能否只用三条与坐标轴平行的直线就能全部被经过,其实只要将横纵坐标排序后逐个点检查下就行. ...
- NOIP2017提高组模拟赛 8(总结)
NOIP2017提高组模拟赛 8(总结) 第一题 路径 在二维坐标平面里有N个整数点,Bessie要访问这N个点.刚开始Bessie在点(0,0)处. 每一步,Bessie可以走到上.下.左.右四个点 ...
- 2017.11.7 Noip2017 考前模拟赛
----------------------------------T1---------------------------------- ——>数学老师的报复 题目描述 11 班数学大佬 Y ...
- [NOIP2018模拟赛10.23]发呆报告
闲扯 考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做 T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过... 比较玄学的一件事情就是T1 ...
- NOIP模拟赛20161022
NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...
- NOI模拟赛 Day1
[考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...
- NOIP第7场模拟赛题解
NOIP模拟赛第7场题解: 题解见:http://www.cqoi.net:2012/JudgeOnline/problemset.php?page=13 题号为2221-2224. 1.car 边界 ...
- contesthunter暑假NOIP模拟赛第一场题解
contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...
随机推荐
- php new self()和new static()
class A { public static function get_self() { return new self(); } public static function get_static ...
- Android Shape总结
Shape的基础标签共有6个:corners, gradient, solid, stroke, padding, size Shape可以有四种形状:rectangle(矩形,默认形状),oval( ...
- Windows 10 IoT Serials 1 - 针对Minnow Board MAX的Windows 10 IoT开发环境搭建
目前,微软针对Windows IoT计划支持的硬件包括树莓派2,Minnow Board MAX 和Galileo (Gen 1和Gen 2).其中,Galileo (Gen 1和Gen 2)运行的是 ...
- Linux SendMail服务启动慢总结
在 CentOS release 6.6 上启动sendmail服务时发现服务启动过程非常慢,基本上要耗费3分多钟.有点纳闷:什么原因导致sendmail启动这么慢?搜索了这方面的一些资料,结合自己的 ...
- YourSQLDba将数据库置于紧急模式的原因浅析
从SQL SERVER 2000 上迁移了一个数据库到SQL SERVER 2008 R2上,暂且用DataBaseName代替迁移的真实的数据库名(后面的资料也会将数据库真实的名字用DataBase ...
- js作用域问题
<script type="text/javascript"> alert(i);//Uncaught ReferenceError: i is not defined ...
- 使用JNI封装底层input系统提供的event事件
首先说下思路,本文采用jni技术封装底层触摸事件,封装成MotionEvent类一样,不过没有android系统MotionEvent强大.源码MotionEvent位置:java-->fram ...
- Redis学习和环境搭建
基本的redis教程,搭建,可以参照下面任一教程: 地址一:http://www.yiibai.com/redis/redis_quick_guide.html 地址二:http://www.runo ...
- 【转】【51CTO 网+】怎样做一款让用户来电的产品
[51CTO 网+]怎样做一款让用户来电的产品 据相关调查显示,目前全球移动用户平均每人安装应用约95个,每天使用的应用约35个.可见面对众多的移动应用,用户拥有非常大的选择空间.如果由于交互设计欠佳 ...
- 洛谷P1328 生活大爆炸版石头剪刀布——S.B.S.
题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一样,则不分胜负.在<生活大爆炸>第二季第8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的 ...