昨日老刘开了最大团专题后,起初以为挺简单,后来发现有点麻烦。能用的资料又很少。分享一下学习的东西
以poj 2989为例,题意是求 不同极大团的数量
要用Bron–Kerbosch算法
首先是最朴素的伪代码,没有任何剪枝(来自维基百科)https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm
BronKerbosch(All, Some, None):
if Some and None are both empty:
report All as a maximal clique
for each vertex v in Some:
BronKerbosch1(All ⋃ {v}, Some ⋂ N(v), None ⋂ N(v))
Some := Some \ {v}
None := None ⋃ {v}
N(v)为顶点v相邻的点。
all为已取的顶点集,some为未处理顶点集(初始状态是全部顶点),none为不取的顶点集
最大团类型的题目中,我们求最大团顶点数时只要some,要求记录路径时要all和some,这里求极大团数量,需要all、some、none。
c代码:
void dfs(int d, int an, int sn, int nn)
//d为搜索深度,an、sn、nn分别为all、some、none集合中顶点数,
{
if(sn == 0 && nn == 0) ++ S;//sn==0搜索到终点,只有nn==0时,才是一个极大团,S为极大团数量
for(int i = 0; i < sn; i ++)
{
int v = some[d][i];
int tsn = 0, tnn = 0;
for(int j = 0; j < an; j ++) all[d + 1][j] = all[d][j];
all[d + 1][an] = v;
for(int j = 0; j < sn; j ++)if(g[v][some[d][j]]) some[d + 1][tsn ++] = some[d][j];
for(int j = 0; j < nn; j ++) if(g[v][none[d][j]]) none[d + 1][tnn ++] = none[d][j];
dfs(d + 1, an + 1, tsn, tnn);
//把v从some取出,放入none
some[d][i] = 0, none[d][nn ++] = v;
}
}
(
原文:
The basic form of the algorithm, described above, is inefficient in the case of graphs with many non-maximal cliques: it makes a recursive call for every clique, maximal or not. To save time and allow the algorithm to backtrack more quickly in branches of the search that contain no maximal cliques, Bron and Kerbosch introduced a variant of the algorithm involving a "pivot vertex" u, chosen from P (or more generally, as later investigators realized,[4] from P ⋃ X). Any maximal clique must include either u or one of its non-neighbors, for otherwise the clique could be augmented by adding u to it. Therefore, only u and its non-neighbors need to be tested as the choices for the vertex v that is added toR in each recursive call to the algorithm. In pseudocode:
)
以上算法太朴素,搜索了大量不是极大团的点集。
为了节省时间和让算法更快的回溯,我们可以通过设定关键点 pivot vertex u。
对于任意的最大团,其必须包括顶点u或者u的非邻接点.否则就可以添加顶点u来扩充极大团,这显然矛盾.所以.我们只要测试 顶点u以及 u的非邻接点即可.这样可以节省递归的时间.
伪代码:
BronKerbosch(All, Some, None):
if Some and None are both empty:
report All as a maximal clique
choose a pivot vertex u in Some ⋃ None
for each vertex v in Some \ N(u)::
BronKerbosch1(All ⋃ {v}, Some ⋂ N(v), None ⋂ N(v))
Some := Some \ {v}
None := None ⋃ {v}
C代码:
void dfs(int d, int an, int sn, int nn)
{
if(sn == 0 && nn == 0) ++ S;
int u = some[d][0];//pivot vertex
for(int i = 0; i < sn; i ++)
{
int v = some[d][i];
if(g[u][v]) continue;
int tsn = 0, tnn = 0;
for(int j = 0; j < an; j ++) all[d + 1][j] = all[d][j];
all[d + 1][an] = v;
for(int j = 0; j < sn; j ++)if(g[v][some[d][j]]) some[d + 1][tsn ++] = some[d][j];
for(int j = 0; j < nn; j ++) if(g[v][none[d][j]]) none[d + 1][tnn ++] = none[d][j];
dfs(d + 1, an + 1, tsn, tnn);
//把v从some取出,放入none
some[d][i] = 0, none[d][nn ++] = v;
}
}poj 2989代码:
/*
TechMonster
02.13.2016
*/
#include<stdio.h>
#include<iostream>
#include<string>
#include <algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#define pi acos(-1.0)
#define ls o<<1
#define rs o<<1|1
#define MS(x,y) memset(x,y,sizeof(x));
<span style="font-family: Arial, Helvetica, sans-serif;">#define MAXN 130</span>
const int MAXN = 0x3f3f3f3f;
const double eps = 1e-8;
void fre(){freopen("t.txt","r",stdin);}
using namespace std;
int N, M, a[MAXN], g[MAXN][MAXN];
int S, all[MAXN][MAXN], some[MAXN][MAXN], none[MAXN][MAXN];
//all为已取顶点集,some为未处理顶点集,none为不取的顶点集
//我们求最大团顶点数时只要some,要求记录路径时要all和some,这里求极大团数量,需要all、some、none
void dfs(int d, int an, int sn, int nn)
{
if(S > 1000) return ; // 极大团数量超过1000就不再统计
if(sn == 0 && nn == 0) ++ S;//sn==0搜索到终点,只有nn==0时,才是一个极大团
int u = some[d][0];//pivot vertex
for(int i = 0; i < sn; i ++)
{
int v = some[d][i];
if(g[u][v]) continue;
int tsn = 0, tnn = 0;
for(int j = 0; j < an; j ++) all[d + 1][j] = all[d][j];
all[d + 1][an] = v;
for(int j = 0; j < sn; j ++)if(g[v][some[d][j]]) some[d + 1][tsn ++] = some[d][j];
for(int j = 0; j < nn; j ++) if(g[v][none[d][j]]) none[d + 1][tnn ++] = none[d][j];
dfs(d + 1, an + 1, tsn, tnn);
//把v从some取出,放入none
some[d][i] = 0, none[d][nn ++] = v;
}
}
void process()
{
S = 0;
for(int i = 0; i < N; i ++) some[0][i] = i + 1;
dfs(0, 0, N, 0);
if(S > 1000) printf("Too many maximal sets of friends.\n");
else printf("%d\n", S);
}
int main()
{
// fre();
while(scanf("%d%d", &N, &M) == 2)
{
MS(g, 0);
for(int i = 0; i < M; i ++)
{
int x, y;
scanf("%d%d", &x, &y);
g[x][y] = g[y][x] = 1;
}
process();
}
return 0;
}
除此之外还有另一种优化方法:
An alternative method for improving the basic form of the Bron–Kerbosch algorithm involves forgoing pivoting at the outermost level of recursion, and instead choosing the ordering of the recursive calls carefully in order to minimize the sizes of the sets P of candidate vertices within each recursive call.
The degeneracy of a graph G is the smallest number d such that every subgraph of G has a vertex with degree d or less. Every graph has a degeneracy ordering, an ordering of the vertices such that each vertex has d or fewer neighbors that come later in the ordering; a degeneracy ordering may be found in linear time by repeatedly selecting the vertex of minimum degree among the remaining vertices. If the order of the vertices v that the Bron–Kerbosch algorithm loops through is a degeneracy ordering, then the set P of candidate vertices in each call (the neighbors of v that are later in the ordering) will be guaranteed to have size at most d. The set X of excluded vertices will consist of all earlier neighbors of v, and may be much larger than d. In recursive calls to the algorithm below the topmost level of the recursion, the pivoting version can still be used.[6][7]
In pseudocode, the algorithm performs the following steps:
BronKerbosch3(G):
P = V(G)
R = X = empty
for each vertex v in a degeneracy ordering of G:
BronKerbosch2(R ⋃ {v}, P ⋂ N(v), X ⋂ N(v))
P := P \ {v}
X := X ⋃ {v}
没怎么研究,有兴趣自己看吧。。
Bron-Kerbosch算法详解与优化:求解图的最大团问题
本文介绍了最大团问题,并以POJ 2989题为例,详细讲解了Bron-Kerbosch算法的基本形式和存在的效率问题。通过引入关键点(pivot vertex)优化算法,避免搜索非极大团,以减少递归的时间。同时,文章提到了利用图的退化度(degeneracy)进行进一步优化,确保候选顶点集合大小不超过退化度,从而提高算法效率。
1717

被折叠的 条评论
为什么被折叠?



