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& ...
随机推荐
- Java异常之自定义异常
哎呀,妈呀,又出异常了!俗话说:"代码虐我千百遍,我待代码如初恋". 小Alan最近一直在忙着工作,已经很久没有写写东西来加深自己的理解了,今天来跟大家聊聊Java异常.Java异 ...
- [转]看懂UML类图
这里不会将UML的各种元素都提到,我只想讲讲类图中各个类之间的关系: 能看懂类图中各个类之间的线条.箭头代表什么意思后,也就足够应对 日常的工作和交流: 同时,我们应该能将类图所表达的含义和最终的代码 ...
- SqlServer--聚合函数
--聚合函数默认把整个表中的数据当做"一组",然后才进行的统计. select * from NewPerson --统计出所有人的年龄的总和 select sum(age) as ...
- .NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,代替DDD实现轻量级业务)
阅读目录: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单示例 4.总结 1.背景介绍 一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之 ...
- spark streaming 与 kafka 结合使用的一些概念理解
1. createStream会使用 Receiver:而createDirectStream不会,数据会通过driver接收. 2.createStream使用 Receiver 源源不断的接收数据 ...
- ASP.NET MVC Razor
Razor是MVC3中才有的新的视图引擎.我们知道,在ASP.NET中,ASPX的视图引擎依靠<%和%>来调用C#指令.而MVC3以后有了一套新的使用@标记的Razor语法,使用起来更灵活 ...
- mysql 一些命令
show processlist; 查看服务进程列表show engines; 所有提供的引擎show variables like '%storage_engine%'; 当前默认的存储引擎show ...
- 编译安装zabbix 3.0及分开部署配置详解
实验系统:CentOS 6.6_x86_64 实验前提:提前准备好编译环境,防火墙和selinux都关闭 实验说明:本实验共有4台主机,IP及角色分配如拓扑 下载地址:试验中用到mariadb软件的下 ...
- 复制粘贴出来的悲剧----spring实现文件下载和HttpStatus.CREATED
今天真是被自己的懒惰和复制粘贴给坑惨了... 网上有这么一个spring下载文件的最佳实践: @RequestMapping("download") public Response ...
- linux上安装subversion
1.安装svn服务器端 yum install subversion 从镜像下载安装svn服务器端 中间会提示是否ok,输入y,确认 安装成功提示:.....complete! 依次执行 ...