您的位置:首页 > 其它

有向图连通分量SCC

2013-12-03 23:51 232 查看
在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通。如果图中任意两个顶点之间都连通,则称该图为连通图,否则,称该图为非连通图,则其中的极大连通子图称为连通分量,这里所谓的极大是指子图中包含的顶点个数极大。
直观地说,极大就是不能再大,或者说再大也不能超过自己。因此,极大连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,且S是S'的子图,可推出S = S',
  则称S是G的极大连通子图。
  极小连通子图正好相反,极小就是不能再小,再多小一点就会不连通或点不足。因此,极小连通子图就是:
  设
  1) S为G的子图,S连通,
  2) 如果有S'也是G的连通子图,S'包含G的所有顶点,且S'是S的子图,可推出S' = S,
  则称S是G的级小连通子图。
  注:这个定义和pinejeely给出的等价。这里给出的定义比较容易验证。
在有向图中,如果对于每一对顶点vi和vj,从vi到vj和从vj到vi都有路径,则称该图为强连通图;否则,将其中的极大强连通子图称为强连通分量。

Kosaraju算法:先dfs,得到最后完成时间f,再求反图,按f递减的方向对反图再dfs一次。

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
using namespace std;
#define N 1000
int head
, headt
, cnt;
struct node
{
int next, to;
}edge[N * 2], edget[N * 2];

void addedge(int from, int to)//G_T
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
//得到反图
edget[cnt].next = headt[to];
edget[cnt].to = from;
headt[to] = cnt;
}
int f
;//finishtime
int d
;//discovertime
int color
;//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int belong
;//which scc
int cur;//current scc
void dfs_visit(int x)
{
color[x] = 1;
d[x] = ++time;
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!color[j])
{
dfs_visit(j);
}
}
//color[x] = 2;
f[x] = ++time;
}

void dfs_visit_t(int x)
{
color[x] = 1;
//d[x] = ++time;
int i, j;
for (i = headt[x]; i; i = edget[i].next)
{
j = edget[i].to;
if (!color[j])
{
dfs_visit_t(j);
}
}
//color[x] = 2;
//f[x] = ++time;
belong[x] = cur;
}

bool cmp(const int &a, const int &b)
{
return a > b;
}

map<int, int, greater<int> > mp;//使用map对f[i]进行从大到小排序
map<int, int>::iterator it;

void init()
{
cnt = cur = 1;
time = -1;
memset(head, 0, sizeof(head));
memset(f, 0, sizeof(f));
memset(d, 0, sizeof(d));
memset(color, 0, sizeof(color));
memset(belong, 0, sizeof(belong));
mp.clear();
}

int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = 1; i <= n; i++)//得到f
if (!color[i])
dfs_visit(i);
//sort(f, f + n, cmp);
for (i = 1; i <=n; i++)//对f排序
mp[f[i]] = i;
memset(color, 0, sizeof(color));
for (it = mp.begin(); it != mp.end(); ++it)//对反图进行dfs
{
if (!color[it->second])
{
dfs_visit_t(it->second);
cur++;
}
}
for (i = 1; i <=n; i++)
printf("%d ", belong[i]);
}
return 0;
}


Tarjan算法只需一次dfs,而且不用求反图,dfs找最早祖先点(最先被发现)也就是寻找B边。

使用LOW函数测试此条件

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#define INF 0x7fffffff
using namespace std;
#define N 1000

stack<int> s;
int head
, cnt;
struct node
{
int next, to;
}edge[N * 2];

void addedge(int from, int to)
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
//int f
;//finishtime
int pre
;//discovertime
//int color
;//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int id
;//which scc
int low
;//
int cur;//current scc
void dfs_visit(int x)
{
low[x] = pre[x] = ++time;
s.push(x);
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!pre[j])
{
dfs_visit(j);
}
if (low[j] < low[x])//找最小的low[x],即是否存在后向边(B边)
low[x] = low[j];
}
if (low[x] == pre[x])//找到了一个scc
{
do
{
i = s.top();
s.pop();
low[i] = INF;
id[i] = cur;
}while (i != x);
cur++;
}
}

void init()
{
cnt = cur = 1;
time = 0;
memset(head, 0, sizeof(head));
memset(pre, 0, sizeof(pre));
memset(low, 0, sizeof(low));
memset(id, 0, sizeof(id));
while (!s.empty())
s.pop();
}

int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = 1; i <= n; i++)
if (!pre[i])
dfs_visit(i);
for (i = 1; i <=n; i++)
printf("%d ", id[i]);
}
return 0;
}


Gabow算法:思路与tarjan思路一样,但是用一个stack替代了low数组,使得交换次数减小

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iomanip>
#include <set>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#define INF 0x7fffffff
using namespace std;
#define N 1000

stack<int> s;
stack<int> p;
int head
, cnt;
struct node
{
int next, to;
}edge[N * 2];

void addedge(int from, int to)
{
cnt++;
edge[cnt].next = head[from];
edge[cnt].to = to;
head[from] = cnt;
}
//int f
;//finishtime
int pre
;//discovertime
//int color
;//init 0 denote white; 1 denote gray discover; 2 denote black finish
int time;
int id
;//which scc
int low
;//
int cur;//current scc
void dfs_visit(int x)
{
low[x] = pre[x] = ++time;
s.push(x);
p.push(x);
int i, j;
for (i = head[x]; i; i = edge[i].next)
{
j = edge[i].to;
if (!pre[j])
dfs_visit(j);
if (!id[j])//该点未在已求的scc中
while (pre[j] < pre[p.top()])存在后向边,出栈
p.pop();
}
if (p.top() == x)//找到一个scc
{
p.pop();
do
{
i = s.top();
id[i] = cur;
s.pop();
}while (i != x);
cur++;
}
}

void init()
{
cnt = cur = 1;
time = 0;
memset(head, 0, sizeof(head));
memset(pre, 0, sizeof(pre));
memset(low, 0, sizeof(low));
memset(id, 0, sizeof(id));
while (!s.empty())
s.pop();
while (!p.empty())
p.pop();
}

int main()
{
int n, m, u, v, i;
while (~scanf("%d%d", &n, &m))
{
init();
for (i = 0; i < m; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for (i = 1; i <= n; i++)
if (!pre[i])
dfs_visit(i);
for (i = 1; i <=n; i++)
printf("%d ", id[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: