简单状压dp的思考 - 最大独立集问题和最大团问题 - 贰
接着上文
题目链接:最大独立集问题
上次说到,一种用状压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的思考 - 最大独立集问题和最大团问题 - 贰的更多相关文章
- 简单状压dp的思考 - 最大独立集问题和最大团问题 - 壹
本文参考:CPH ,USACO Guide (大佬请越过,这是初学笔记,不要吐槽内容) 前置知识:位运算基础,动态规划基础 介绍 状态是元素的子集的动态规划算法,可以用位运算来高效的优化. 那么第一道 ...
- POJ 3254 简单状压DP
没什么可说的,入门级状压DP.直接撸掉 #include <iostream> #include <cstring> #include <cstdlib> #inc ...
- poj2411 Mondriaan's Dream[简单状压dp]
$11*11$格子板上铺$1*2$地砖方案.以前做过?权当复习算了,毕竟以前学都是浅尝辄止的..常规题,注意两个条件:上一行铺竖着的则这一行同一位一定要铺上竖的,这一行单独铺横的要求枚举集合中出现连续 ...
- Walk Through Squares HDU - 4758 AC自动机+简单状压DP
题意:给你两个串,求用m个R,n个D能组成多少个包含这两个串 题解:先构造一个AC自动机记录每个状态包含两个串的状态, 状态很容易定义 dp[i][j][k][status]表示在AC自动机K这个节点 ...
- [Usaco2008 Nov]mixup2 混乱的奶牛 简单状压DP
1231: [Usaco2008 Nov]mixup2 混乱的奶牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 685 Solved: 383[S ...
- 2018.09.22 牧场的安排(状压dp)
描述 农民 John 购买了一处肥沃的矩形牧场,分成M*N(1 <= M <= 12; 1 <= N <= 12)个 格子.他想在那里的一些格子中种植美味的玉米.遗憾的是,有些 ...
- 2019牛客多校第五场 F maximum clique 1 状压dp+最大独立集
maximum clique 1 题意 给出一个集合s,求每个子集的最大独立集的权值和(权值是独立集的点个数) 分析 n比较小,一股浓浓的暴力枚举每一个子集的感觉,但是暴力枚举模拟肯定会T,那么想一想 ...
- ZOJ3802 Easy 2048 Again (状压DP)
ZOJ Monthly, August 2014 E题 ZOJ月赛 2014年8月 E题 http://acm.zju.edu.cn/onlinejudge/showProblem.do?proble ...
- POJ 1185 炮兵阵地(状压DP)
炮兵阵地 Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 26426 Accepted: 10185 Descriptio ...
随机推荐
- 基于语义感知SBST的API场景测试智能生成
摘要:面对庞大服务接口群,完备的接口测试覆盖和业务上下文场景测试看护才有可能保障产品服务的质量和可信.如果你想低成本实现产品和服务的测试高覆盖和高质量看护,这篇文章将为你提供你想要的. 本文分享自华为 ...
- XCTF练习题---CRYPTO---Broadcast
XCTF练习题---CRYPTO---Broadcast flag:flag{fa0f8335-ae80-448e-a329-6fb69048aae4} 解题步骤: 1.观察题目,下载附件 2.下载完 ...
- GO语言学习——切片三 append()方法为切片添加元素、切片的扩容策略、使用copy()函数复制切片、从切片中删除元素
append()方法为切片添加元素 注意:通过var声明的零值切片可以在append()函数直接使用,无需初始化. var s []int s = append(s, 1, 2, 3) append( ...
- Docker容器的安装和使用
Docker容器的安装和使用 安装: curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 或国内:curl -sSL ...
- 论文解读《Bilinear Graph Neural Network with Neighbor Interactions》
论文信息 论文标题:Bilinear Graph Neural Network with Neighbor Interactions论文作者:Hongmin Zhu, Fuli Feng, Xiang ...
- 戏说领域驱动设计(廿七)——Saga设计模型
上一节我们讲解了常用的事务,也提及了Saga,这是在分布式环境下被经常使用的一种处理复杂业务和分布式事务的设计模式.本章我们的主要目标是编写一个简单版本的Saga处理器,不同于Seata框架中那种可独 ...
- 【单片机】使用 sscanf 提取AT命令返回结果中的有效数据
摘要:1. sscanf函数 sscanf是C标准库函数,用于从字符串中读取格式化输入. 头文件: #include <stdio.h>函数原型如下: int sscanf(const c ...
- 注意:Spring Boot 2.7开始spring.factories不推荐使用了,接下来这么玩...
如果你是Spring Boot用户的话,一定有这样的开发体验,当我们要引入某个功能的时候,只需要在maven或gradle的配置中直接引入对应的Starter,马上就可以使用了,而不需要像传统Spri ...
- 使用argparse进行调参
argparse是深度学习项目调参时常用的python标准库,使用argparse后,我们在命令行输入的参数就可以以这种形式python filename.py --lr 1e-4 --batch_s ...
- PostgreSQL 13支持增量排序(Incremental Sorting)
PostgreSQL 13支持增量排序(Incremental Sorting) PostgreSQL 13一个重要的功能是支持增量排序,使用order by 时可以加速排序,SQL如下 select ...