求强连通分量的基础题,用来练一下刚搞懂的Tarjan:
Tarjan算法的过程就是不断避免把桥纳入强连通分量中
注意到以下性质:
1,桥一定是DFS树中的边
2,一条树边v-w为桥,当且仅当不存在回边将w的一个子孙与w的一个祖先相连
#include
<
iostream
>
#include
<
stack
>
using
namespace
std;
#define
Max(a,b)(a<b?b:a)
#define
MAXN 101
int
p[MAXN],ecnt,n,c,ideg[MAXN],odeg[MAXN];
int
lowlink[MAXN],
//
结点所处强连通分量的代表结点,也是具有最小时间截的结点(在DFS树中深度最浅)(动态更新)
dfn[MAXN],
//
访问时间(固定)
sign,
//
时间截
SCC[MAXN];
//
结点所处强连通分量
bool
visited[MAXN],instack[MAXN];
stack
<
int
>
st;
struct
Edge{
int
v,next;
}edg[
10000
];
void
update(
int
&
a,
int
b){
a
=
(a
<
b
?
a:b);
}
void
dfs(
int
u){
int
i,v;
st.push(u);
visited[u]
=
instack[u]
=
true
;
lowlink[u]
=
dfn[u]
=++
sign;
for
(i
=
p[u];i
!=-
1
;i
=
edg[i].next){
v
=
edg[i].v;
if
(visited[v]){
if
(instack[v]){
update(lowlink[u],bfn[v]);
//
u-v是返祖边
}
}
else
{
dfs(v);
update(lowlink[u],lowlink[v]);
}
}
if
(lowlink[u]
==
dfn[u]){
//
若u是代表结点,则深度比其深的仍在栈内的待处理结点均属于该强连通分量
c
++
;
do
{
v
=
st.top();
st.pop();
instack[v]
=
false
;
SCC[v]
=
c;
}
while
(v
!=
u);
}
}
void
Tarjan(){
int
i;
c
=
0
;
sign
=
0
;
memset(visited,
false
,
sizeof
(visited));
memset(instack,
false
,
sizeof
(instack));
for
(i
=
1
;i
<=
n;i
++
)
if
(
!
visited[i])
dfs(i);
}
void
solve(){
int
i,u,v,ic,oc;
Tarjan();
//
求强连通分量
memset(ideg,
0
,
sizeof
(ideg));
memset(odeg,
0
,
sizeof
(odeg));
for
(u
=
1
;u
<=
n;u
++
){
for
(i
=
p[u];i
!=-
1
;i
=
edg[i].next){
v
=
edg[i].v;
if
(SCC[u]
!=
SCC[v]){
odeg[SCC[u]]
++
;
ideg[SCC[v]]
++
;
}
}
}
if
(c
==
1
){
printf(
"
1\n0\n
"
);
return
;
}
ic
=
oc
=
0
;
for
(i
=
1
;i
<=
c;i
++
){
if
(ideg[i]
==
0
)
ic
++
;
if
(odeg[i]
==
0
)
oc
++
;
}
printf(
"
%d\n%d\n
"
,ic,Max(ic,oc));
}
int
main(){
int
u,v;
while
(scanf(
"
%d
"
,
&
n)
!=
EOF){
while
(
!
st.empty())
st.pop();
ecnt
=
0
;
memset(p,
-
1
,
sizeof
(p));
for
(u
=
1
;u
<=
n;u
++
){
while
(scanf(
"
%d
"
,
&
v)
&&
v){
edg[ecnt].v
=
v;
edg[ecnt].next
=
p[u];
p[u]
=
ecnt
++
;
}
}
solve();
}
return
0
;
}