「图论」Bron-kerbosch算法
7.21晚上加赛 T2.七负我,做这题找到了性质发现需要求最大团,不会,爆搜,打假了,赛后改,对了,但时间复杂度大爆炸,看下发题解,有这么一句话:
于是学习了一下。
Bron-kerbosch算法-求图的最大团,极大团
概念:
- 团:每个顶点都两两相连(又叫完全子图)
- 极大团:没有被包含在其他团中的团
- 最大团:顶点数最多的极大团
算法过程:
过程:
我们维护三个集合 \(R,P,X\),\(R\) 表示当前正在找的极大团里的点,\(P\) 表示有可能加入当前在找的极大团里的点,\(X\) 表示已经找到的极大团中的点(用来判重),进行以下过程:
初始化 \(R, X\) 为空集,\(P\) 为包含所有点的集合;
将 \(P\) 中顶部元素 \(u\) 点取出,(设 \(Q(u)\) 为所有与 \(u\) 相邻的点)递归集合 \(R ∪{u},P ∩ {Q(u)},X ∩ {Q(u)}\);
- 在递归的过程中如果集合 \(P 和 X\) 都为空,则集合 \(R\) 中的点构成一个极大团。
将 \(u\) 点从集合 \(P\) 中删去,添加到集合 \(X\) 中;
不断重复 2~3 操作,直至 \(P\) 为空。
只看算法过程可能不好理解,那么下面是伪代码及分析。
伪代码(伪代码出处CSDN已改进):
void dfs(R, P, X){
if(P 和 X 均为空) 输出 R 集合为一个极大团
for 从 P 中选取一个点 a,与 a 相连的点集为 Q(a) {
dfs(R 并上 a,P 和 Q(a) 的交集,X 和 Q(a) 的交集)
从 P 中移除 a 点
把 a 点加入 X 集合
}
}
分析:
算法主要思路:很简单,我们每次枚举合法的点加入极大团中,合法即为保证该点加入团中,该团仍然是团,接着更新合法点集合(即可能属于在找的团的点集 \(P\) ),不断递归直到该团极大即可。
我们用 \(P\) 集合维护可能包含于目前所在找的极大团的点集,分析 \(P\) 集合是如何更进的:
\(R\) 是当前在找的极大团,由于 \(R\) 集合是每次任意从 \(P\) 中取一个点,我们知道团的定义为任意两个点都有边相连,所以若我把当前新选择的点 \(a\) 加入团中,那么 \(R\) 加入 \(a\) 之后,要想保证新 \(P\) 集合中的点可能包含于新 \(R\) 中团,那么需要满足 \(P\) 中的点都与 \(R\) 中任意一点相连。我们已经可以保证原 \(R\) (加入 \(a\) 之前)集合里所有点都与原 \(P\) 中的点相连,所以现在只需添加条件使得新 \(P\) 中的点与 \(a\) 点相连,于是 \(P∩{Q(a)}\) 是新 \(P\) 集合。找到一个极大团时需要满足 \(P,X\) 集合都为空:
\(P\) 为空即再没有点可以加到 \(R\) 集合中,保证在找的团极大;\(X\) 为空保证之前没有找过此团,用来判重。
算法实现:
带详细注释code:
注:建议先看本篇博客的算法过程部分以方便看懂代码的注释
int to[N][N], mnt; //to[i][j]用来判断 i 到 j 之间是否连边,mnt为最大团中点的个数
int had[N][N], may[N][N], vis[N][N]; //had,may,vis分别表示 当前在找的团中已有的点、可能加入当前在找的团中的点、已经搜过的点(分别对应算法过程的集合 R,P,X)
//had,may,vis的第一维i都表示处于搜索的第i层,第二维j表示相应的点的个数
//d表示当前搜索处于第几层,R、P、X分别表示had,may,vis在该层搜索中点的个数
void Bron_Kerbosch(int d, int R, int P, int X){
if(!P and !X){ mnt = max(mnt, R); return;} //找到一个极大团
for(int i=1; i<=P; i++){
int u = may[d][i]; //从 P 中取点
for(int j=1; j<=R; j++){
had[d+1][j] = had[d][j];
} had[d+1][R+1] = u; //即 R' = R + {u} 的操作
int newP = 0, newX = 0;
for(int j=1; j<=P; j++) // P' = P ∩ Q(u)
if(to[u][may[d][j]]) may[d+1][++newP] = may[d][j];
for(int j=1; j<=X; j++) // X' = X ∩ Q(u)
if(to[u][vis[d][j]]) vis[d+1][++newX] = vis[d][j];
Bron_Kerbosch(d+1, R+1, newP, newX); //递归搜索
may[d][i] = 0, vis[d][++X] = u; //将 u 点从 P 中删去,加入 X 中
}
}
到这里,就已经可以 A 掉那晚加赛的 T2.七负我 了。
AC 代码
#include<bits/stdc++.h>
#define mp make_pair
#define ll long long
using namespace std;
const int N = 50;
int n, m, x, hnt;
int to[N][N];
int had[N][N], may[N][N], vis[N][N];
void Bron_Kerbosch(int d, int R, int P, int X){
if(!P and !X){ hnt = max(hnt, R); return; }
for(int i=1; i<=P; i++){
int u = may[d][i];
for(int j=1; j<=R; j++){
had[d+1][j] = had[d][j];
} had[d+1][R+1] = u;
int newP = 0, newX = 0;
for(int j=1; j<=P; j++)
if(to[u][may[d][j]]) may[d+1][++newP] = may[d][j];
for(int j=1; j<=X; j++)
if(to[u][vis[d][j]]) vis[d+1][++newX] = vis[d][j];
Bron_Kerbosch(d+1, R+1, newP, newX);
may[d][i] = 0, vis[d][++X] = u;
}
}
signed main(){
// freopen("in.in", "r", stdin); freopen("out.out", "w", stdout);
scanf("%d%d%d", &n, &m, &x);
for(int i=1; i<=m; i++){
int a, b; scanf("%d%d", &a, &b);
to[a][b] = to[b][a] = 1;
}
int num = 0;
for(int i=1; i<=n; i++)
may[1][++num] = i;
Bron_Kerbosch(1, 0, num, 0);
double ans = x * 1.0 / hnt;
ans *= ans;
ans *= ((hnt - 1) * hnt / 2);
printf("%.6lf", ans);
return 0;
}
但是,这个算法还可以通过设定关键点(pivot vertex)\(v\) 进行优化。主要优化原理见 oi-wiki。
优化代码(纯享版):
int to[N][N], hnt;
int had[N][N], may[N][N], vis[N][N];
void Bron_kerbosch(int d, int R, int P, int X){
if(!P and !X) { hnt = max(hnt, R); return;}
int u = may[d][1];
for(int i=1; i<=P; i++){
int v = may[d][i];
if(to[u][v]) continue;
for(int j=1; j<=R; j++){
had[d+1][j] = had[d][j];
} had[d+1][R+1] = v;
int newP = 0, newX = 0;
for(int j=1; j<=P; j++)
if(to[v][may[d][j]]) may[d+1][++newP] = may[d][j];
for(int j=1; j<=X; j++)
if(to[v][vis[d][j]]) vis[d+1][++newX] = vis[d][j];
Bron_kerbosch(d+1, R+1, newP, newX);
may[d][i] = 0, vis[d][++X] = v;
}
}
「图论」Bron-kerbosch算法的更多相关文章
- 不设目标也能通关「马里奥」的AI算法,全靠好奇心学习
在强化学习中,设计密集.定义良好的外部奖励是很困难的,并且通常不可扩展.通常增加内部奖励可以作为对此限制的补偿,OpenAI.CMU 在本研究中更近一步,提出了完全靠内部奖励即好奇心来训练智能体的方法 ...
- 「POI2010」反对称 Antisymmetry (manacher算法)
# 2452. 「POI2010」反对称 Antisymmetry [题目描述] 对于一个 $0/1$ 字符串,如果将这个字符串 $0$ 和 $1$ 取反后,再将整个串反过来和原串一样,就称作「反对称 ...
- 「给产品经理讲JVM」:垃圾收集算法
纠结的我,给我的JVM系列终于起了第三个名字,害,我真是太难了.从 JVM 到 每日五分钟,玩转 JVM 再到现在的给产品经理讲 JVM ,虽然内容为王,但是标题可以让更多的人看到我的文章,所以,历经 ...
- 「2014-3-18」multi-pattern string match using aho-corasick
我是擅(倾)长(向)把一篇文章写成杂文的.毕竟,写博客记录生活点滴,比不得发 paper,要求字斟句酌八股结构到位:风格偏杂文一点,也是没人拒稿的.这么说来,arxiv 就好比是 paper 世界的博 ...
- jvm系列(十):如何优化Java GC「译」
本文由CrowHawk翻译,是Java GC调优的经典佳作. 本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三 ...
- 洛谷 P4714 「数学」约数个数和 解题报告
P4714 「数学」约数个数和 题意(假):每个数向自己的约数连边,给出\(n,k(\le 10^{18})\),询问\(n\)的约数形成的图中以\(n\)为起点长为\(k\)的链有多少条(注意每个点 ...
- 一个「学渣」从零开始的Web前端自学之路
从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...
- LoibreOJ 2042. 「CQOI2016」不同的最小割 最小割树 Gomory-Hu tree
2042. 「CQOI2016」不同的最小割 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 ...
- 「NOI2018」你的名字
「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...
- 【LibreOJ】#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop 线段树+完全背包
[题目]#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop [题意]给定一个长度为n的物品序列,每个物品有价值.不超过m的重量.要求支持以下三种操作:1.物品价值区间加减,2.物 ...
随机推荐
- Vue——方法(methods)
我们用 methods 选项向组件实例添加方法,它应该是一个包含所需方法的对象: <div id="app"></div> <script> c ...
- WPF开发快速入门【0】前言与目录
前言 WPF是一个生不逢时的技术,刚推出的时候由于是XP时代,WPF技术有两个不方便的地方: 1.由于操作系统没有自带Framework,需要另外安装,比较麻烦: 2.程序第一次启动时,由于要加载Fr ...
- latex图片格式问题解决(viso转PDF转eps)
latex图片格式问题解决(viso->PDF->eps) 1.viso绘图 如何让界面自动适应图的大小? 设计->大小->适应绘图 如何将VISIO图转换为PDF? 文件-& ...
- 10W QPS高并发,如何防止重复下单?
小北说在前面 10wqps高并发,如何防止重复提交/支付订单? 10wqps高并发,如何防止重复下单? 10wqps高并发,如何防止重复支付? 10wqps高并发,如何解决重复操作问题? 最近有小伙伴 ...
- python——入门
操作系统 操作系统就是一个特殊的软件 作用有两个,第一个:直接操作硬件:第二个:把操作硬件的代码封装成一个又一个的系统调用 python的特点 python是完全面向对象的语言 函数,模块,数字,字符 ...
- OAuth + Security - 4 - 客户端信息存储数据库
PS:此文章为系列文章,建议从第一篇开始阅读. 在之前的所有配置中,我们的客户端信息和授权码模式下的授权码任然还是存储在数据库中的,这样就不利于我们后期的扩展,所以在正式的生成环境中,我们一般将其存储 ...
- Mac 上切换 node 版本总结
Mac 上切换 node 版本总结 背景 在做项目的时候,往往会遇到老项目没有升级的问题,node环境版本比较低,自己电脑安装或者新项目安装了最新版本的 node 环境,那么既要支持老版本的使用也要支 ...
- 随机二次元图片API第三弹
Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 随机二次元图片API第三弹 日期:2020-3-10 阿珏 ...
- Linux实时查看Java接口数据
1.Linux实时查看Java接口数据的方法 在Linux系统中实时查看Java接口数据通常涉及几个步骤: (1)编写Java应用程序:首先,我们需要有一个Java应用程序,它暴露了一个或多个HTTP ...
- Jmeter自动录制脚本
1.Jmeter配置 1.1新增一个线程组 1.2Jmeter中添加HTTP代理 1.3配置HTTP代理服务器 修改端口 修改Target Cintroller(目标控制器) 修改Grouping(分 ...