接着上文

题目链接:最大独立集问题

上次说到,一种用状压DP解决任意无向图最大团问题(MCP)的方程是:

注:此处popcountmax代表按照二进制位下1的个数作为关键字比较,即选择二进制位下1的个数多的那一个

\(F_S =popcountmax \{ F_{S/{k}} , F_{i \in (E_k\cap S)} \cup \{k\} \}\)

其中k是S中任选的一个点。可以证明这样一定是最优的。

首先来细细说说这个算法的复杂度。

外层枚举子图S,假设点数为n,找出k的所有属于S的邻居(与之有边直接连接的点)的点集合需要 \(\mathcal O(n)\) 时间,总共为\(\mathcal O(2^nn)\)

其实这里可以把\(\mathcal O(n)\)优化掉。那么显然是要在找邻居上做些工夫了。

n很小,可以用邻接矩阵建图。只不过,把矩阵变成一维,用一维数组的位掩码表示邻居集合。

程序中可以用位掩码来表示较小的集合子集。

比如\((10010)_2 = 18\)这个二进制数字可以表示第1,4个元素选中(从0开始计数),0,2,3未被选中。

计算机中有高效的并行的位运算,计算位运算时会加速。合理运用位运算可以得到\(\mathcal O(\dfrac{n}{32})\)或\(\mathcal O(\dfrac{n}{64})\)这样的复杂度(我也不知道渐进记号能不能这样写:)

如果要求出\(E_k\cap S\)只需将k的邻居位掩码与S的位掩码做集合与操作就可以了。

输入并建立补图的位掩码代码实现

scanf("%d%d",&n,&m);
for(i=1; i<=m; i++)
scanf("%d%d",&u,&v),gr[u][v]=gr[v][u]=1;
for(i=0; i<n; i++)
for(j=0; j<n; j++)
if(!gr[i][j] && i!=j)
a[i]|=1ll<<j;

MCP的DP:

#define pop __builtin_popcountll
#define ctz __builtin_ctzll
#define ll long long ll popmax(ll x,ll y){if(pop(x)<pop(y))return y;else return x;} void mcp(int x,ll f[]) {
for(int i=0,c,d; i<(1ll<<x); i++) {
d=i&(-i);
c=ctz(d);
f[i]=popmax(f[i^d],f[(i^d)&a[c]]|d);
}
}

__builtin_popcountll 可以快速得出long long型数的二进制位下1的个数,可以看做 \(\mathcal O(1)\)。

__builtin_ctzll 可以快速得出long long型数的末尾0的个数,也可以看做 \(\mathcal O(1)\)。

i&(-i)是一种获取i中其中一位的方法,其它的也可以,然后就能解决 \(n \leq 26\) 的部分了。

要达到更优一点的复杂度要用到折半搜索或者在CodeForces上叫做 Meet-in-the-middle

把原图分成两部分,分别做MCP的DP,之后进行合并:假如把分成的两部分说成是S1,S2,那么遍历S1中的点,找到S2中与S1中所有点都有直接连边的点的集合,做集合并操作。这里的问题不在于集合并操作,而在于找到S2中与S1中所有点都有直接连边的点的集合。倘若与S1中所有点都有直接连边的点的集合用\(G_{S_1}\)表示,那么\(G_{S_1} = G_{S_1/{k}} \cap E_k\),进一步用\(G_{S_1}\cap S_2\)求出S2中与S1中所有点都有直接连边的点的集合。

复杂度都是\(\mathcal O(\sqrt{2^n})\)

最后存边要用另外的方法,下面是可AC的完整代码:

(因为最初发在Guide上,注释英文,有错望告知)

#include <cstdio>
#define ll long long
#define pop __builtin_popcountll
#define ctz __builtin_ctzll
int gr[2006][1010];
ll a[2006];
ll popmax(ll x,ll y){if(pop(x)<pop(y))return y;else return x;}
void mcp(int x,int dt,ll f[]){for(int i=0,c,d;i<(1<<x);i++) d=i&(-i),c=ctz(d),f[i]=popmax(f[i^d],f[(i^d)&a[c+dt]>>(dt)]|d);} //(Supplementary notes)Bitmask DP to solve MCP in O(2^n)
//f[S] = any possible MIS in S subgraph (f[S] is a set)
//choose any point in S called D
//if D isn't chosen F[S] = F[S/{D}]
//otherwise F[S] = F[D's neighbour in S]∪{D} void print_mask(ll ans){printf("%d\n",pop(ans));while(ans) printf("%d ",ctz(ans)),ans-=ans&(-ans);}
ll f[1<<22],f2[1<<22],com[1<<22];
int main(){
int n,m,u,v,i,j,n1,n2;
scanf("%d%d",&n,&m),n1=n/2,n2=n-n1;
for(i=1;i<=m;i++) scanf("%d%d",&u,&v),gr[u][v]=gr[v][u]=1;
for(i=0;i<n;i++) for(j=0;j<n;j++) if(!gr[i][j]&&i!=j) a[i]|=1ll<<j;
//1.use bit mask to represent complement graph
//now is to solve a Maximum Clique Problem(MCP) mcp(n1,0,f),mcp(n2,n1,f2);
//2.Bitmask DP ll ans=0;
for(com[0]=(1ll<<n)-1,i=1ll;i<(1ll<<n1);i++) com[i]=com[i-(i&(-i))]&a[ctz(i)];
for(i=0;i<(1ll<<n1);i++) ans=popmax(ans,(f2[com[i]>>n1]<<n1)|f[i]);print_mask(ans);
//3.Meet-int-the-middle
//loop every subsets called S1,construct S2
//of all nodes of the second half which have edges to all the nodes in S1
//(so that together they still form a clique shared by both halves)
//find the maximum answer
return 0;
}

COCI 2016 Burza

IOI 2007 Training

上面两道题目也是星级推荐的题目,如果练练手挺适合的。

简单状压dp的思考 - 最大独立集问题和最大团问题 - 贰的更多相关文章

  1. 简单状压dp的思考 - 最大独立集问题和最大团问题 - 壹

    本文参考:CPH ,USACO Guide (大佬请越过,这是初学笔记,不要吐槽内容) 前置知识:位运算基础,动态规划基础 介绍 状态是元素的子集的动态规划算法,可以用位运算来高效的优化. 那么第一道 ...

  2. POJ 3254 简单状压DP

    没什么可说的,入门级状压DP.直接撸掉 #include <iostream> #include <cstring> #include <cstdlib> #inc ...

  3. poj2411 Mondriaan's Dream[简单状压dp]

    $11*11$格子板上铺$1*2$地砖方案.以前做过?权当复习算了,毕竟以前学都是浅尝辄止的..常规题,注意两个条件:上一行铺竖着的则这一行同一位一定要铺上竖的,这一行单独铺横的要求枚举集合中出现连续 ...

  4. Walk Through Squares HDU - 4758 AC自动机+简单状压DP

    题意:给你两个串,求用m个R,n个D能组成多少个包含这两个串 题解:先构造一个AC自动机记录每个状态包含两个串的状态, 状态很容易定义 dp[i][j][k][status]表示在AC自动机K这个节点 ...

  5. [Usaco2008 Nov]mixup2 混乱的奶牛 简单状压DP

    1231: [Usaco2008 Nov]mixup2 混乱的奶牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 685  Solved: 383[S ...

  6. 2018.09.22 牧场的安排(状压dp)

    描述 农民 John 购买了一处肥沃的矩形牧场,分成M*N(1 <= M <= 12; 1 <= N <= 12)个 格子.他想在那里的一些格子中种植美味的玉米.遗憾的是,有些 ...

  7. 2019牛客多校第五场 F maximum clique 1 状压dp+最大独立集

    maximum clique 1 题意 给出一个集合s,求每个子集的最大独立集的权值和(权值是独立集的点个数) 分析 n比较小,一股浓浓的暴力枚举每一个子集的感觉,但是暴力枚举模拟肯定会T,那么想一想 ...

  8. ZOJ3802 Easy 2048 Again (状压DP)

    ZOJ Monthly, August 2014 E题 ZOJ月赛 2014年8月 E题 http://acm.zju.edu.cn/onlinejudge/showProblem.do?proble ...

  9. POJ 1185 炮兵阵地(状压DP)

    炮兵阵地 Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 26426   Accepted: 10185 Descriptio ...

随机推荐

  1. python学习-Day36

    目录 进程 进程间通信 队列(multiprocess.Queue) 概念 Queue的实例q具有以下方法: 消息队列 生产者消费者模型 实现生产者消费者模型三要素: 线程理论 线程概念 线程存在的意 ...

  2. 【ConcurrentHashMap】浅析ConcurrentHashMap的构造方法及put方法(JDK1.7)

    目录 引言 代码讲解 构造方法 put方法 ensureSegment Segment.put 引言 ConcurrentHashMap的数据结构如下. 和HashMap的最大区别在于多了一层Segm ...

  3. 干货|SQL语句大全,所有的SQL都在这里了(建议收藏)

    一个执着于技术的公众号 一.基础 1.登录数据库 mysql -uroot -p123123 2.创建数据库 create database <数据库名> 3.删除数据库 drop dat ...

  4. CentOS 7上搭建Zabbix4.0

    开源Linux 一个执着于技术的公众号 zabbix介绍 Zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.它能监视各种网络参数,保证服务器系统的安全运 ...

  5. 分享一下 Idea 的 scope 功能

    分享一下 Idea 的 scope 功能 事情的起因是我在使用 idea 的call hierarchy功能时,觉得它没有像find usage那样有排除功能,并且如果点击了展开全部,当代码中使用了某 ...

  6. 小米 pro 笔记本双硬盘设置引导盘

    功能键 F2 进入 BIOS F12 进入 Boot 选项 步骤 小米 Pro 默认是开启了 UEFI,如果 Boot 选项没有显示出期望的系统盘,那么就是这个系统盘没有 UEFI 分区,按照这个文档 ...

  7. DevOps之敏捷开发

    初步了解一下敏捷开发及其流程 1 为什么要敏捷开发? 敏捷开发描述了一套软件开发的价值和原则,在这些开发中,需求和解决方案皆通过自组织跨功能团队达成. 1.1 背景与动机 当需求的不明确性和工程实现的 ...

  8. 109_Power Pivot客户ABC(帕累托)分析度量值写法(非计算列)

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 1.背景 客户ABC分析,一般的套路是在计算列中把客户ABC分类,便于后续维度使用.今天用度量值的方式写一个ABC的分类. ...

  9. HTML5续集

    H5 input新增属性 1.color 拾色器 2.Email 电子邮件 3.tel 电话 4.datetime-local 本地日期和时间 5.range 范围 6.url 路径,地址 7.sea ...

  10. C#与SQL Server连接时,如何编写连接字符串?

    一.Windows身份验证时: String conStr = "Data Source=数据库服务器地址;Initial Catalog=数据库名称;Integrated Security ...