博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
poj2762
阅读量:5934 次
发布时间:2019-06-19

本文共 3243 字,大约阅读时间需要 10 分钟。

强连通分支+拓扑排序

求强连通分支有两种方法,korasaju和tarjan。

korasaju是进行两次dfs覆盖全图(实际上是两种dfs,覆盖全图需要多次dfs),第一次给结点标起始和结束时间,第二次把图反向并从结束时间最大的结点开始dfs,每次dfs所能到达的结点为一个强连通分支。下面来简单讲一下为什么这样做。第二次dfs过程中每次dfs的起点为结束时间最大的点,这样就可以保证是第一次dfs中的某一棵搜索树的根节点。那么从任意一个第一次dfs的根节点开始的对反图的dfs都不可能跨越到在第一次dfs中比该根节点更早的搜索树中。因为假设能跨越,说明反图中晚的搜索树有指向更早搜索树的边,即原图中有从更早搜索树指向晚搜索树的边。那么晚搜索树根本就不会单独成树,与之前假设矛盾。这样就保证了第二次dfs过程中每次dfs只可能在第一次的某一棵子树中进行,不可能跨树搜索。第二次能根u搜到的点v,说明原图中v可以到达u,而第一次v一定是u树中的结点,也就是说u可到达v,从而一定能形成强连通分支。

tarjan算法则是利用一次dfs,整个过程中对每个结点记录两个值,dfn[u]结点的访问时间,low[u]结点及其子节点所能访问到的结点v中dfn[v]的最小值。每次把访问到的结点入栈,一旦搜索完某一结点u的所有子节点后发现low[u]==dfn[u]则弹栈直到u被弹出,此过程弹出的点为一个强连通分支。这样也就保证了,栈中所有结点都是可以到达某一父节点的,也就是说一旦某个点可以到达栈中某个点,它一定可以到达某父节点。

题意:给出一个有向图,要求该图中任意两节u,v,要么能从u走到v,要么能从v走到u。问是否满足。

分析:

强连通分支中的点一定能互相到达。不属于同一强连通分支的点就用拓扑排序来判断,如果某次删除点的时候发现两个入度为0的点,则说明这两个点只能由已经被删掉的点到达,也就是说这两个点互相不可达。

得到强连通分支后重新建图的方法是,新建一个存储图的空间。用每个强连通分支的编号作为新图中结点的编号。判断原来的每条边的两端点是否属于同意强连通分支,若不属于则在新图中加入一条连接两个强连通分支编号对应结点的边。

View Code
#include
<
iostream
>
#include
<
cstdlib
>
#include
<
cstring
>
#include
<
cstdio
>
using
namespace
std;
#define
maxn 1005
#define
maxm 5005
struct
Edge
{
int
v, next;
} edge[maxm], opedge[maxm], newedge[maxm];
int
n, m, head[maxn], ophead[maxn], ncount, nowtime, pos[maxn
*
2
], sig[maxn], signnum,
in
[maxn], newhead[maxn];
bool
flag[maxn], g[maxn][maxn];
void
addedge(
int
a,
int
b)
{
edge[ncount].v
=
b;
edge[ncount].next
=
head[a];
head[a]
=
ncount;
opedge[ncount].v
=
a;
opedge[ncount].next
=
ophead[b];
ophead[b]
=
ncount;
ncount
++
;
}
void
input()
{
scanf(
"
%d%d
"
,
&
n,
&
m);
for
(
int
i
=
0
; i
<
m; i
++
)
{
int
a, b;
scanf(
"
%d%d
"
,
&
a,
&
b);
a
--
;
b
--
;
addedge(a, b);
}
}
void
dfs(
int
a)
{
flag[a]
=
true
;
pos[nowtime]
=
a;
nowtime
++
;
for
(
int
i
=
head[a]; i
!=
-
1
; i
=
edge[i].next)
if
(
!
flag[edge[i].v])
dfs(edge[i].v);
pos[nowtime]
=
a;
nowtime
++
;
}
void
rdfs(
int
a)
{
flag[a]
=
true
;
sig[a]
=
signnum;
for
(
int
i
=
ophead[a]; i
!=
-
1
; i
=
opedge[i].next)
if
(
!
flag[opedge[i].v])
rdfs(opedge[i].v);
}
bool
ok()
{
bool
found;
int
a;
memset(flag,
0
,
sizeof
(flag));
while
(
1
)
{
found
=
false
;
for
(
int
i
=
0
; i
<
signnum; i
++
)
if
(
in
[i]
==
0
&&
!
flag[i])
{
if
(found)
return
false
;
found
=
true
;
flag[i]
=
true
;
a
=
i;
}
if
(
!
found)
break
;
for
(
int
i
=
newhead[a]; i
!=
-
1
; i
=
newedge[i].next)
in
[newedge[i].v]
--
;
}
return
true
;
}
int
main()
{
//
freopen("D:\\t.txt", "r", stdin);
int
t;
scanf(
"
%d
"
,
&
t);
while
(t
--
)
{
ncount
=
0
;
memset(head,
-
1
,
sizeof
(head));
memset(ophead,
-
1
,
sizeof
(ophead));
memset(flag,
0
,
sizeof
(flag));
input();
nowtime
=
1
;
for
(
int
i
=
0
; i
<
n; i
++
)
if
(
!
flag[i])
dfs(i);
memset(flag,
0
,
sizeof
(flag));
signnum
=
0
;
for
(
int
i
=
2
*
n; i
>
0
; i
--
)
if
(
!
flag[pos[i]])
{
rdfs(pos[i]);
signnum
++
;
}
memset(g,
0
,
sizeof
(g));
memset(
in
,
0
,
sizeof
(
in
));
memset(newhead,
-
1
,
sizeof
(newhead));
ncount
=
0
;
for
(
int
i
=
0
; i
<
n; i
++
)
for
(
int
j
=
head[i]; j
!=
-
1
; j
=
edge[j].next)
if
(sig[i]
!=
sig[edge[j].v]
&&
!
g[sig[i]][sig[edge[j].v]])
{
int
a
=
sig[i];
int
b
=
sig[edge[j].v];
g[a][b]
=
true
;
in
[b]
++
;
newedge[ncount].v
=
b;
newedge[ncount].next
=
newhead[a];
newhead[a]
=
ncount;
ncount
++
;
}
if
(ok())
printf(
"
Yes\n
"
);
else
printf(
"
No\n
"
);
}
return
0
;
}

转载地址:http://sfctx.baihongyu.com/

你可能感兴趣的文章
Proteus仿真_01、 8086 IO译码仿真
查看>>
在centOS上安装VNC
查看>>
Leetcode: Paint House
查看>>
2016年第16周日
查看>>
IntelliJ IDEA创建web项目
查看>>
LeetCode Recover Binary Search Tree——二查搜索树中两个节点错误
查看>>
巧用Reponse.Filter实现多语言功能
查看>>
[zz]python 目录操作
查看>>
linux tomcat自启动设置
查看>>
App架构设计学习(一)---- 接口的设计
查看>>
angularjs中ng-class的使用
查看>>
CMD命令名详细大全
查看>>
Vue.js 入门指南之“前传”(含sublime text 3 配置)
查看>>
ArcGIS Engine开发之旅02--ArcGIS Engine中的类库
查看>>
源码编译失败的时候,重新编译
查看>>
Android开发之SD卡上文件操作
查看>>
Charles抓包https
查看>>
c++ 读写Excel及数据导入SQLServer
查看>>
改造 Android 官方架构组件 ViewModel
查看>>
“地球一小时”?看看“熊孩子”怎么说
查看>>