请问如何求(有向/无向)图的强连通分量,还有,基础一点,怎么求有几个连通图啊
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了请问如何求(有向/无向)图的强连通分量,还有,基础一点,怎么求有几个连通图啊相关的知识,希望对你有一定的参考价值。
不太想花时间学习tarjan算法了,麻烦介绍个简单的思路,能应付复赛的时候几个数据就好了
求强连通分量的算法有tarjan和kosaraju 两种算法相较之下 tarjan写起来比较简单 Kosaraju比较麻烦
但是想起来 Kosaraju比较简单
其他求强连通分量的算法 要是还有的话 估计就是需要更高深的数据结构的算法了
建议还是学下tarjan 因为他可以帮你做很多事 比如 求桥 求割点 缩环 而且写起来也很简单
连通图的求法可以直接DFS 每次DFS到一个点 就把它记录成已到达 然后继续向下搜索 每次DFS就可以求出一个连通图
附上tarjan的代码
var
next,head,point:array[1..1000] of longint;
time,tot,i,j,n,m,x,y,t:longint;
v:array[1..10000] of byte;
f,z,q:array[1..1000] of longint;
low,rea:array[1..10000] of longint;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
procedure add(x,y:longint);
begin
inc(tot);
next[tot]:=head[x];
head[x]:=tot;
point[tot]:=y;
end;
procedure dfs(x:Longint);
var
i,j:longint;
begin
inc(time);
low[x]:=time;
rea[x]:=time;
v[x]:=1;
inc(t);
z[t]:=x;
j:=head[x];
while j<>0 do
begin
if v[point[j]]=0 then dfs(point[j]);
if v[point[j]]<2 then low[x]:=min(low[point[j]],low[x]);
j:=next[j];
end;
if low[x]=rea[x] then
begin
inc(tot);
while z[t+1]<>x do
begin
inc(q[tot]);
f[z[t]]:=tot;
v[z[t]]:=2;
dec(t);
end;
end;
end;
begin
readln(n,m);
for i:=1 to m do
begin
readln(x,y);
add(x,y);
end;
tot:=0; time:=0;
for i:=1 to n do
if v[i]=0 then dfs(i);
//writeln(tot);
for i:=1 to n do
if q[f[i]]<>1 then writeln('T') else writeln('F');
end. 参考技术A 寻找强烈连接组件的算法有Tarjan的,kosaraju两种算法
相比Tarjan的写了相对简单Kosaraju的太麻烦了
但认为它是相对简单
Kosaraju其他要求强烈连接组件如果有,估计需要更先进的数据结构算法的算法
建议或学校根据Tarjan的,因为他可以帮助你做很多事情,如寻求桥切点减少环,但也写一些简单的 BR p>连通图的方法可以直接DFS每个DFS一个点把它记录已经达到了,然后继续向下搜索,每次DFS可以计算出一个连通图
附加Tarjan的代码</ VAR
下,头,点:数组[1 .. 1000] Longint型;
时间,TOT,I,J,N,M,X,Y,T:Longint型;
V:ARRAY [1 .. 10000]字节;
F,Z,Q:ARRAY [1 .. 1000] Longint型;
低,原因:[1阵列。 0.10000] Longint型;
函数min(X,Y:Longint型):Longint型;
开始
如果x <y,那么退出(X)其他出口(Y);
结束;
程序地址(X,Y:Longint型);
开始
公司(TOT);
下一个[合计]:=头[X]
头[X]:= TOT
点[合计]:= Y;
结束;
程序DFS(X:Longint型);
VAR
I,J: Longint型;
公司(时间)开始;
低[X]:=时间;
原因[X]:=时间;
V [X]:= 1
公司(T);
Z [T]:= X;
:=头[X]
而J > 0
开始
V [点[J] = 0,那么(DFS点[J]);
如果v [点[J] <2低[X]:= MIN(下限[点[ J],低[X]);
J:=未来[J];
结束;
低[X] =原因[X],然后
开始...... / a>公司(TOT);
而Z [T +1] > X你
开始
公司(Q [合计]);
F [Z [T]] := TOT
V [Z [T]]:= 2;
十二月(T);
结束;
结束;
结束;
开始
我:= 1米做
开始
readln(X,Y);
加载(,Y readln(N,M))
结束;
TOT:= 0;时间:= 0;
我:= 1到n做
V [I] = 0,则DFS(I);
/ / writeln(TOT);
我:= 1到n做
如果q [F [I]]> 1,则writeln('T')其他writeln('F ');
结束。
图论有向图的强连通分量
目录
有向图的强连通分量
连通分量: 对于分量中任意两点
u
,
v
u,v
u,v,必然可以从
u
u
u走到
v
v
v,且从
v
v
v走到
u
u
u。
强连通分量(
S
C
C
SCC
SCC): 极大连通分量。一个连通分量加上任何一些点都不是连通分量了,该连通分量就是强连通分量。
强连通分量的作用: 将任意有向图通过 缩点(将所有连通分量缩成一个点) 转换成有向无环图(
D
A
G
DAG
DAG)。
常见应用:对于上图,将有向图缩点之后,可以直接按照拓扑序递推来求最短路/最长路
如何求强连通分量( T a r j a n Tarjan Tarjan算法)
D
F
S
DFS
DFS
一些概念:
边可以分为四大类:
1.树枝边
(
x
,
y
)
(x,y)
(x,y)。
x
x
x是
y
y
y的父节点
2.前向边(
x
x
x,
y
y
y)。
x
x
x是
y
y
y的祖先节点
3.后向边(
x
x
x,
y
y
y)
4.横叉边(往之前搜过的其他分支搜,连向其他分支的边)
如果一个点在强连通分量(
S
C
C
SCC
SCC)中
情况1:存在一条后向边,指向祖先结点
情况2:先走到横叉边,横叉边再走到祖先节点
Tarjan算法求强连通分量(
S
C
C
SCC
SCC)
引入时间戳的概念,在搜索的时候给每一个点一个编号(按照深度优先搜索的顺序)
对每个点定义两个时间戳:
d
f
n
[
u
]
dfn[u]
dfn[u]表示遍历到
u
u
u的时间戳
l
o
w
[
u
]
low[u]
low[u]表示从
u
u
u开始走,所能遍历到的最小的时间戳
u
u
u是其所在的强连通分量的最高点,等价于
d
f
n
[
u
]
dfn[u]
dfn[u]==
l
o
w
[
u
]
low[u]
low[u]
tarjan算法模板
void tarjan(int u)
//dfn是当前点的时间戳,low是该点能够到达的最小的时间戳
dfn[u]=low[u]=++timestamp;
stk.push(u),in_stk[u]=true;//将当前点加入栈当中
for(int i=head[u];~i;i=ne[i])//遍历u所有能到的点
int v=e[i];
if(!dfn[v])//如果该点还没有被遍历过
tarjan(v);
low[u]=min(low[u],low[v]);//更新最小的时间戳
else if(in_stk[v])low[u]=min(low[u],dfn[v]);//如果v点还在栈中,就用这个点来更新low值
if(dfn[u]==low[u])//u是该强连通分量的最高点
++scc_cnt;
int y;
do
y=stk.top();stk.pop();//将该强连通分量的所有点出栈
in_stk[y]=false;//不在栈中了
id[y]=scc_cnt;//标记该点的强连通分量的下标
Size[scc_cnt]++;//该连通分量的大小加1
while(y!=u);
时间复杂度 O ( n + m ) O(n+m) O(n+m)
//缩点
for i=1;i<=n;i++
for i的所有邻点j
if i和j不在同一scc中:
加一条新边id[i]→id[j]
形成一个有向无环图
(
D
A
G
)
(DAG)
(DAG)
缩点之后,强连通分量编号点按编号递减的顺序就是拓扑序。
假设对于一个点
u
u
u,当我们执行if(dfn[u]==low[u])
这句话时,说明
u
u
u点所能到的点都搜完了,也就是这个点的所有后继都搜完了,我们才将这个点所在序列当中(也就是标记为
s
c
c
_
c
n
t
scc\\_cnt
scc_cnt),逆序来看的话,所有这个点的后继都在这个点的前面,那必然就是拓扑序了。
受欢迎的牛
原题链接
每一头牛的愿望就是变成一头最受欢迎的牛。
现在有
N
N
N头牛,编号从
1
1
1到
N
N
N,给你
M
M
M对整数
(
A
,
B
)
(A,B)
(A,B),表示牛
A
A
A认为牛
B
B
B受欢迎。
这种关系是具有传递性的,如果
A
A
A认为
B
B
B受欢迎,
B
B
B认为
C
C
C受欢迎,那么牛
A
A
A也认为牛
C
C
C受欢迎。
你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。
输入格式
第一行两个数
N
,
M
N,M
N,M;
接下来
M
M
M行,每行两个数
A
,
B
A,B
A,B,意思是
A
A
A认为
B
B
B是受欢迎的(给出的信息有可能重复,即有可能出现多个
A
,
B
A,B
A,B)。
输出格式
输出被除自己之外的所有牛认为是受欢迎的牛的数量。
数据范围
1
≤
N
≤
1
0
4
1≤N≤10^4
1≤N≤104,
1
≤
M
≤
5
×
1
0
4
1≤M≤5×10^4
1≤M≤5×104
输入样例:
3 3
1 2
2 1
2 3
输出样例:
1
样例解释
只有第三头牛被除自己之外的所有牛认为是受欢迎的。
分析:
题目意思就是需要找到的牛是能够被其他所有牛所能到达的,直接暴力的话时间复杂度太大。
如果用拓扑图来说,这个题会变得简单,如果当前图是一个拓扑图,如果至少存在两个终点(没有出边,出度为
0
0
0),那么这两个终点不可达,那么答案为
0
0
0;如果只有一个点出度为
0
0
0,那么所有点都能走到这个点。所以对于拓扑图,我们只需要判断有几个出度为
0
0
0的点即可。那么本题,我们将一个图的强连通分量缩点成拓扑图,再进行判断。
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10005,M=50005;
int n,m;
int head[N],e[M],ne[M],tot;
int dfn[N],low[N],timestamp;
stack<int>stk;
bool in_stk[N];
int id[N],scc_cnt,Size[N];
int dout[N];
void add(int a,int b)
e[tot]=b,ne[tot]=head[a],head[a]=tot++;
void tarjan(int u)
//dfn是当前点的时间戳,low是该点能够到达的最小的时间戳
dfn[u]=low[u]=++timestamp;
stk.push(u),in_stk[u]=true;
for(int i=head[u];~i;i=ne[i])
int v=e[i];
if(!dfn[v])//如果该点还没有被遍历过
tarjan(v);
low[u]=min(low[u],low[v]);//更新最小的时间戳
else if(in_stk[v])low[u]=min(low[u],dfn[v]);
if(dfn[u]==low[u])//u是该连通分量的最上面的点
++scc_cnt;
int y;
do
y=stk.top();stk.pop();//将该连通分量的所有点出栈
in_stk[y]=false;//不在栈中了
id[y]=scc_cnt;//标记该点的连通分量的下标
Size[scc_cnt]++;//该连通分量的大小加1
while(y!=u);
int main()
scanf("%d %d",&n,&m);
memset(head,-1,sizeof(head));
while(m--)
int a,b;
scanf("%d %d",&a,&b);
add(a,b);
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);//缩点
for(int i=1;i<=n;i++)
for(int j=head[i];~j;j=ne[j])
int k=e[j];
int a=id[i],b=id[k];//遍历每两个点,如果这两个点不在一个连通分量中,出度加1,相当于缩点了
if(a!=b)dout[a]++;
int zeros=0,sum=0;
for(int i=1;i<=scc_cnt;i++)
if(!dout[i])//出度为0的点只能有一个,最后的结果就是出度为0的那个连通分量的大小
zeros++;
sum+=Size[i];
if(zeros>1)
sum=0;
break;
printf("%d\\n",sum);
return 0;
学校网络
原题链接
一些学校连接在一个计算机网络上,学校之间存在软件支援协议,每个学校都有它应支援的学校名单(学校
A
A
A支援学校
B
B
B,并不表示学校
B
B
B一定要支援学校
A
A
A)。
当某校获得一个新软件时,无论是直接获得还是通过网络获得,该校都应立即将这个软件通过网络传送给它应支援的学校。
因此,一个新软件若想让所有学校都能使用,只需将其提供给一些学校即可。
现在请问最少需要将一个新软件直接提供给多少个学校,才能使软件能够通过网络被传送到所有学校?
最少需要添加几条新的支援关系,使得将一个新软件提供给任何一个学校,其他所有学校就都可以通过网络获得该软件?
输入格式
第
1
1
1行包含整数
N
N
N,表示学校数量。
第
2..
N
+
1
2..N+1
2..N+1行,每行包含一
以上是关于请问如何求(有向/无向)图的强连通分量,还有,基础一点,怎么求有几个连通图啊的主要内容,如果未能解决你的问题,请参考以下文章