您的位置:首页 > 其它

强联通分量:Kosarajus算法

2011-07-14 14:21 323 查看
以前接触过,也花了不少时间弄懂,不过当时只是写了一道题,代码也不知道放哪了,几个月下来忘得差不多了。

今日回顾,有温故而知新的感觉。

算法的证明与理解如下,具体的网上/书上都有(摘自《数据结构与算法分析》)

由于V是X在Gr的深度优先搜索树中的一个后裔,因此存在Gr中一条从X到V的路径,从而存在G中中一条从V到X的路径。此外,由于X是根节点,因此X从第一次深度优先搜索得到更高的后续编号。于是,在第一次深度优先搜索期间所有处理V的工作都在X的工作结束前完成。既然存在一条从V到X的路径,因此V必然是X在G的生成树中的一个后裔——否则V将在X之后结束。这意味着G中从X到V有一条路径,证明完成。

我写的代码很丑,不知道怎么命名反向边及相关操作,于是就在后面加了一个r。

下面是POJ的三道入门题,基本一样,第一道用邻接矩阵存储,后两道用邻接表存储,都是最简单的缩点然后求出入度有关的。具体的写在注释里了。

POJ 1236

{ http://www.cppblog.com/mythit/archive/2009/05/25/85718.html 这里写得不错,就复制一下吧

题目大意:N(2<N<100)各学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输,问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。2,至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

具体算法:先用Korasaju Algorithm求出有向图所有的强连通分量,然后将所有的强连通分量缩成一个点(缩点),这样原来的有向图就缩成了一个DAG图(有向无环图);用2个数组分别记录新生成的DAG图中的每个顶点(包括原来的顶点和强连通分量的缩点)是否有出边和入边,最后遍历每个顶点,如果没有入边,则ans1++;如果没有出边,ans2++。最后所求即为ans1和max(ans1,ans2)。

犯错:第二次DFS顺序弄反了,另外第一个问题不能用输出强联通分量个数,因为不同强联通分量之间是可以传递的。

2011-07-14 09:50
}

#include <stdio.h>
#define MAXN 200

int g[MAXN][MAXN];
int gr[MAXN][MAXN];
int in[MAXN],out[MAXN],order[MAXN];
int belong[MAXN];
int vis[MAXN];
int count, num, i, j, k, n;

int max(int a, int b)
{
return a > b ? a : b;
}

void init()
{
int i, j;
scanf("%d", &n);
for (i = 1; i <= n; i++)
while (scanf("%d", &j) && j)
{
g[i][j] = 1;
gr[j][i] = 1;
}
}

void dfs(int v)
{
vis[v] = 1;
int i;
for (i = 1; i <= n; i++)
if ((!vis[i]) && g[v][i])
dfs(i);
order[++num] = v;
}

void dfsr(int v)
{
vis[v] = 1;
belong[v] = count;
int i;
for (i = 1; i <= n; i++)
if ((!vis[i]) && gr[v][i])
dfsr(i);
}

void kosaraju()
{
for (i = 1; i <= n; i++)
if (!vis[i])
dfs(i);
memset(vis, 0, sizeof(vis));
for (i = n; i >= 1; i--)
if (!vis[order[i]])
{
count ++;
dfsr(order[i]);
}
}

void calc()
{
int i, j, leaf = 0, root = 0;

for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (g[i][j] && belong[i] != belong[j])
{
in[belong[j]]++;
out[belong[i]]++;
}
for (i = 1; i <= count; i++)
{
if (!in[i])
root++;
if (!out[i])
leaf++;
}
if (count == 1)
printf("1\n0\n");
else
printf("%d\n%d\n", root, max(root, leaf));
}

int main()
{
init();
kosaraju();
calc();
return 0;
}


.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

POJ 2186

/*
来自http://www.cppblog.com/RyanWang/archive/2009/02/26/74984.html

题目简述:
n头奶牛,给出若干个欢迎关系a b,表示a欢迎b,
欢迎关系是单向的,但是是可以传递的。
另外每个奶牛都是欢迎他自己的。
求出被所有的奶牛欢迎的奶牛的数目。

模型转换:
N个顶点的有向图,有M条边(N≤10000,M≤50000)。
求一共有多少个点,满足这样的条件:
所有其它的点都可以到达这个点。

算法:求强连通分量,计算每个分量的出度,如果只有一个为0则该强联通分量的节点个数就是答案,否则无解。

*/

#include <stdio.h>
#define MAXN 100000
#define MAXM 1000000

int e[MAXM],next[MAXM];
int er[MAXM],nextr[MAXM];
int g[MAXN],gr[MAXN];
int size, sizer;

int n, m, i, j, k, x, y, count, number;
int out[MAXN];
int order[MAXN];
int vis[MAXN];
int belong[MAXN];
int num[MAXN];

void insert(int x, int y)
{
e[++size] = y;
next[size] = g[x];
g[x] = size;
}

void insertr(int x, int y)
{
er[++sizer] = y;
nextr[sizer] = gr[x];
gr[x] = sizer;
}

void init()
{
scanf("%d%d", &n, &m);
for (i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
insert(x, y);
insertr(y, x);
}
}

void dfs(int v)
{
vis[v] = 1;
int p;
for (p = g[v]; p; p = next[p])
if (!vis[e[p]])
dfs(e[p]);
order[++number] = v;
}

void dfsr(int v)
{
vis[v] = 1;
belong[v] = count;
int p;
for (p = gr[v]; p; p = nextr[p])
if (!vis[er[p]])
dfsr(er[p]);
}

void kosaraju()
{
for (i = 1; i <= n; i++)
if (!vis[i])
dfs(i);
memset(vis, 0, sizeof(vis));
for (i = n; i >= 1; i--)
if (!vis[order[i]])
{
count++;
dfsr(order[i]);
}
}

int calc()
{
int i, j, k, p, ans = 0;
for (i = 1; i <= n; i++)
num[belong[i]]++;
for (i = 1; i <= n; i++)
for (p = g[i]; p; p = next[p])
if (belong[e[p]] != belong[i])
out[belong[i]]++;
for (i = 1; i <= count; i++)
{
if (ans && !out[i])
return 0;
if (!out[i])
ans = num[i];
}
return ans;
}

int main()
{
init();
kosaraju();
printf("%d\n", calc());
return 0;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

POJ 2553

/*
题目出得比较恶心,下了一大堆定义。总之就是求所有满足这个条件的点:
它能到达的点也能到达它。于是可以进行强连通分量缩点,
只要该点满足所属的强连通分量的出度为0即为所求点。

细节的错误犯了一些,有时候打错一个变量真的很难找啊。

2011年7月14日14:01:57
*/

#include <stdio.h>
#define MAXN 100000
#define MAXM 1000000

int e[MAXM],er[MAXM],next[MAXM],nextr[MAXM];
int g[MAXN],gr[MAXN];
int order[MAXN],vis[MAXN],out[MAXN],belong[MAXN];

int m, n, i, j, k, x, y;
int count, num;
int size, sizer;

void insert(int x, int y)
{
e[++size] = y;
next[size] = g[x];
g[x] = size;
}

void insertr(int x, int y)
{
er[++sizer] = y;
nextr[sizer] = gr[x];
gr[x]  = sizer;
}

void dfs(int v)
{
vis[v] = 1;
int p;
for (p = g[v]; p; p = next[p])
if (!vis[e[p]])
dfs(e[p]);
order[++num] = v;
}

void dfsr(int v)
{
vis[v] = 1;
belong[v] = count;
int p;
for (p = gr[v]; p; p = nextr[p])
if (!vis[er[p]])
dfsr(er[p]);
}

void kosaraju()
{
for (i = 1; i <= n; i++)
if (!vis[i])
dfs(i);
memset(vis, 0,sizeof(vis));
for (i = n; i >= 1; i--)
if (!vis[order[i]])
{
count++;
dfsr(order[i]);
}
}

void init()
{
memset(g, 0, sizeof(g));
memset(gr, 0, sizeof(gr));
memset(belong, 0, sizeof(belong));
memset(order, 0, sizeof(order));
memset(vis, 0, sizeof(vis));
memset(out, 0, sizeof(out));
size =  0;
sizer = 0;
num = 0;

for(i = 1; i <= m; i++)
{
scanf("%d%d", &x, &y);
insert(x, y);
insertr(y, x);
}
}

void calc()
{
int i, p;

//   for (i = 1; i <= n; i++)
//       printf("%d ",belong[i]);
for (i = 1; i <= n; i++)
for (p = g[i]; p; p = next[p])
if (belong[i] != belong[e[p]])
out[belong[i]]++;
for (i = 1; i <= n; i++)
if (!out[belong[i]])
printf("%d ",i);

printf("\n");
}

int main()
{
scanf("%d", &n);
while (n != 0)
{
scanf("%d", &m);
init();
kosaraju();
calc();
scanf("%d", &n);
}
return 0;
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: