求强连通分量的基础题,用来练一下刚搞懂的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 ;
}
#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 ;
}