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& ...
随机推荐
- MySQL ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)的真正原因
在博客Linux mysql 5.6: ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: N ...
- [翻译]当分发数据库增长到25G时如何解决
有时候,分发数据库(Distribution Database)会增长得非常大,那么如何解决呢,请看Chris Skorlinski, Microsoft SQL Server Escalation ...
- Failed to create AppDomain 'xxx'. Exception has been Failed to create AppDomain
一服务器上的数据库全部被置于紧急模式(EMERGENCY),在错误日志里面能看到大量下面的错误 Failed to create AppDomain "YourSQLDba.dbo[runt ...
- Ant :Property
Property Ant 内置的Property 系统属性 Ant附加的属性 自定义Property Ant :Property properties是由key-value组成的集合,就是Java中 ...
- MongoDB学习笔记~为IMongoRepository接口添加分页取集合的方法
回到目录 对于数据分页,我们已经见的太多了,几乎每个列表页面都要用到分页,这已经成了一种定理了,在进行大数据展示时,如果不去分页,而直接把数据加载到内存,这简直是不可以去相向的,呵呵,在很多ORM工具 ...
- C++基础——模拟事务 (1)COMMAND模式
=================================版权声明================================= 版权声明:原创文章 禁止转载 请通过右侧公告中的“联系邮 ...
- PHP笔记(PHP初级篇)
学习完HTML和CSS后,终于要开始学习PHP啦!前面的铺垫只为后路的畅顺! PHP环境搭建: 企业中常用到的环境是:Linux+Apache+MySQL+PHP 学习环境是:Windows+Apac ...
- 关于IPB帧与恒定比特率、动态比特率的详解
之所以写这篇文章是因为有朋友对IPB帧的设置比较感兴趣,回复中说得比较简单,因此在这里详细的写一下,虽然说一般情况下我们很少去设置这个IPB帧,不过,如果真的学好了,并且清楚的了解了这个IPB帧的概念 ...
- java HashMap
HashMap 的性能因子 1. 容量:表示桶位的数量. 2. 初始容量: 表在创建是所拥有的桶位数. 如果你知道将要在HashMap存储多少项,创建一个初始容量合适的HashMap将可以避免自动 ...
- 发一份shiro标准配置,特此记录
主要还是整合了本地ehcache,集群session管理过段时间放出 <?xml version="1.0" encoding="UTF-8"?> ...