首页 > 编程语言 >「图论」Bron-kerbosch算法

「图论」Bron-kerbosch算法

时间:2024-07-23 20:58:32浏览次数:16  
标签:图论 极大 int 团中 may 算法 kerbosch Bron 集合

7.21晚上加赛 T2.七负我,做这题找到了性质发现需要求最大团,不会,爆搜,打假了,赛后改,对了,但时间复杂度大爆炸,看下发题解,有这么一句话:于是学习了一下。


Bron-kerbosch算法-求图的最大团,极大团

概念:

  • :每个顶点都两两相连(又叫完全子图)
  • 极大团:没有被包含在其他团中的团
  • 最大团:顶点数最多的极大团



算法过程:

过程:

我们维护三个集合 \(R、P、X\),\(R\) 表示当前正在找的极大团里的点,\(P\) 表示有可能加入当前在找的极大团里的点,\(X\) 表示已经找到的极大团中的点(用来判重),进行以下过程:

  1. 初始化 \(R、 X\) 为空集,\(P\) 为包含所有点的集合;

  2. 将 \(P\) 中顶部元素 \(u\) 点取出,(设 \(Q(u)\) 为所有与 \(u\) 相邻的点)递归集合 \(R ∪{u},P ∩ {Q(u)},X ∩ {Q(u)}\);

    • 在递归的过程中如果集合 \(P 和 X\) 都为空,则集合 \(R\) 中的点构成一个极大团。

  3. 将 \(u\) 点从集合 \(P\) 中删去,添加到集合 \(X\) 中;

  4. 不断重复 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;

    }
}

标签:图论,极大,int,团中,may,算法,kerbosch,Bron,集合
From: https://www.cnblogs.com/YuenYouth/p/18316868

相关文章

  • 图论基础与遍历算法
    图的逻辑结构及其实现图是由节点和边构成的,边分为有向边和无向边,对应有向图和无向图,逻辑结构如下:根据这个逻辑结构,我们可以实现每个节点: //节点需要存储自身的值,也需要存储与其邻接的节点 structVertex{   intval;//自身值 vector<Vertex*>neighbors;/......
  • 图论-深度优先搜索
    引入DFS全称是DepthFirstSearch,中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。该算法讲解时常常与BFS并列,但两者除了都能遍历图的连通块以外,用途完全不同,很少有能混用两种算法的情况。DFS常常用来指代用递归函数......
  • 【图论】【模板】差分约束系统
    差分约束系统差分约束系统是将不等式组的问题转化为图论问题。前置知识判断负环例题P5960【模板】差分约束算法思路我们将\(x_u-x_v\ley_u\)换为\(x_u\lex_v+y_u\)。然后我们建立一条连接\(v,u\)(注意是\(v,u\)不是\(u,v\))权值为\(y_u\)的边。我们发......
  • 【图论】【模板】最长路、最短路
    最短路Dijkstra算法思路Dijkstra算法,采用贪心思想,在某一时刻如果\(dis\)数组中\(dis_u\)最小,那么就固定\(u\),\(dis_u\)一定是\(1\rightarrowu\)的最短路径,然后我们再通过\(u\)更新与\(u\)有边相连的\(v\),如果\(dis_v>dis_u+w\),那么\(dis_v=dis_u+w\)......
  • 【图论】【模板】判断负环
    使用SPFA算法判断负环前言判断负环是属于判定性的问题,常与二分结合起来。例题AcWing852.spfa判断负环思路可以使用SPFA进行判断。因为两点之间至多有\(n-1\)条边,所以当一个点的最短路径经过的边数大于等于\(n\)时,说明有负环。代码#include<bits/stdc++.h>......
  • [文录] Bronya19C
    在我印象中,czz一直是一个个性活泼、阳光开朗的男生,他常常与别人一同讨论问题,和各个学校的选手一起说话聊天,善于结交朋友。czz同时是一个信息学的强者。自初中以来,一直是全佛山OI的顶梁柱。初二时的他就已经取得了NOIP一等奖,初三的他更是在CSP-S2021中勇夺全省第十六名的......
  • 算法 图论最短路径
    零、写在前面本文讲述Dijkstra、Bellman-Ford、Floyd-Warshall算法一、分类G(graph):图V(vertex):点E(edge):边一个图可以用数学语言描述为。W(weights):权所以一个图也可以用数学语言描述为。二、作图2.1作图网站(推荐) 在线作图网站:图论作图网站GraphEditor用法:Undirected......
  • 【2024-ZR-C Day 4】图论(1)
    1.强连通分量1.1.定义在有向图中,选取一个点集\(S\),若对于\(S\)中的任意两点\(u,v\),都满足\(u\)可以到达\(v\),则称\(S\)是强连通的。强连通分量是图中一个极大的强连通的点集。性质:把一个有向图通过强连通分量缩点后,新的图是一个DAG.1.2.Kosaraju算法在无向图......
  • (nice!!!)LeetCode 3112. 访问消失节点的最少时间(图论、边的dijkstra、堆优化)
    3112.访问消失节点的最少时间思路:节点n的个数非常大,用普通的dijkstra算法对节点进行枚举是会超时的,时间复杂度为0(n^2)。这里边的数量最大为10^5,可以对边使用dijkstra算法+堆优化操作,时间复杂度为0(mlogm)。节点消失问题,只需要加一个判断条件,判断到每个节点的最小时......
  • 浅谈图论
    图的基本概念多个点连成的线就构成了图图的种类(加权)有向图   (加权)无向图度无向图中有几条边连接该节点,该节点就有几度有向图中,每个节点有出度和入度出度:从该节点出发的边的个数入度:指向该节点边的个数 连通性连通图无向图中,任何两个节点都是可以到达的,我们称......