题意:给定一个初始集合和目标集合,有两种操作: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[和谐社会模拟赛]分裂的更多相关文章

  1. 2014-11-2 NOIP模拟赛1

    Noip2009 团结模拟赛如题目理解困难,请自行阅读或参考样例.内存限制均为 256MB,时间限制均为 1s.出题人不会 故意 在题目中设置陷阱,但请自己注意程序的正确性.IO 文件名(.in/.o ...

  2. NOIP一系列模拟赛小结

    NOIP越发接近了,于是自己也跟着机房的几位师兄一起做了几次NOIP模拟赛,收获颇多. #1-T1:求点集中的点能否只用三条与坐标轴平行的直线就能全部被经过,其实只要将横纵坐标排序后逐个点检查下就行. ...

  3. NOIP2017提高组模拟赛 8(总结)

    NOIP2017提高组模拟赛 8(总结) 第一题 路径 在二维坐标平面里有N个整数点,Bessie要访问这N个点.刚开始Bessie在点(0,0)处. 每一步,Bessie可以走到上.下.左.右四个点 ...

  4. 2017.11.7 Noip2017 考前模拟赛

    ----------------------------------T1---------------------------------- ——>数学老师的报复 题目描述 11 班数学大佬 Y ...

  5. [NOIP2018模拟赛10.23]发呆报告

    闲扯 考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做 T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过... 比较玄学的一件事情就是T1 ...

  6. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  7. NOI模拟赛 Day1

    [考完试不想说话系列] 他们都会做呢QAQ 我毛线也不会呢QAQ 悲伤ING 考试问题: 1.感觉不是很清醒,有点困╯﹏╰ 2.为啥总不按照计划来!!! 3.脑洞在哪里 4.把模拟赛当作真正的比赛,紧 ...

  8. NOIP第7场模拟赛题解

    NOIP模拟赛第7场题解: 题解见:http://www.cqoi.net:2012/JudgeOnline/problemset.php?page=13 题号为2221-2224. 1.car 边界 ...

  9. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

随机推荐

  1. 使用 Fiddler 上传微信公众账号 自定义菜单

    0.你必须有微信公众账号的服务号.成为开发者之后.... 1.得到你的 appid (xxxxxxoooo)和 secret (oooooooxxxxxxx) 2.用这个链接得到你的 access_t ...

  2. SQL Server跨库查询

    方式一: 语句 SELECT * FROM 数据库A.dbo.表A a, 数据库B.dbo.表B b WHERE a.field=b.field "DBO"可以省略 如 SELEC ...

  3. C#winfrom播放器动态加载歌词

    上周我们进行了结业项目答辩,是播放器项目.有一个关于播放器变唱歌边加载歌词的方法特别有意思,像酷狗那样子歌词和歌曲同步滚播的样子. 这里的工具是Visual Studio 2013,使用语言是C#和. ...

  4. 《java JDK7 学习笔记》之继承与多态

    1.面向对象中,子类继承父类,避免重复的行为定义,不过并非为了避免重复定义行为就使用继承.应该正确判断使用继承的时机及继承之后灵活的运用多态,才是学习继承时的重点. 2.程序代码重复在程序设计上,就是 ...

  5. 分布式搜索引擎ElasticSearch+Kibana (Marvel插件安装详解)

    在安装插件的过程中,尤其是安装Marvel插件遇到了很多问题,要下载license.Marvel-agent,又要下载安装Kibana 版本需求 Java 7 or later Elasticsear ...

  6. StarkSoft题库管理系统(二)--生成word格式试卷

    一.功能介绍    1.自定义试题库管理系统目录.难易程度,题型,知识库等.    2.试题录入.    3.强大的试题编辑功能,并与通常应用编辑工具有共通.    4.灵活的试卷构造功能,用户可自定 ...

  7. 关于PB调用Microsoft Web Browser控件的一些问题

    Microsoft Web Browser控件是WINDOWS系统自带的控件,一般不需要单独安装,由于工作的需要,把使用中遇到的问题记录一下,以便查阅. 插入控件: 环境为PB12.0,insert- ...

  8. day1学习

    python 2 和 3 的区别 1.用户输入的命令:python 2 中是raw_input(""):python 3 中是input(""). 2.默认字符 ...

  9. Linux系统有7个运行级别(runlevel)

    Linux系统有7个运行级别(runlevel) 运行级别0:系统停机状态,系统默认运行级别不能设为0,否则不能正常启动 运行级别1:单用户工作状态,root权限,用于系统维护,禁止远程登陆 运行级别 ...

  10. chm文件突然乱码了

    今天打开一个chm文件的帮组文档时,文档时乱码,以前可不是的哦.不知道自己干什么操作了.上网找了下解决办法. 打开cmd 输入regedit 进入注册表:找到: HKEY_CURRENT_USER\S ...