該題也可以用雙向dfs求強連通分量,這裡給出Tarjan算法的模板,更適合變形。
也貼上雙向dfs的代碼吧。
代碼Tarjan(這是網上有注釋的代碼,講得很細):
[cpp]
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define MAXN 10010
#define MAXM 100010
struct Edge
{
int v, next;
}edge[MAXM]; //邊結點數組
int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]頭結點數組,stack[]為棧,DFN[]為深搜次序數組,Belong[]為每個結點所對應的強連通分量標號數組
// Low[u]為u結點或者u的子樹結點所能追溯到的最早棧中結點的次序號
int instack[10010]; // instack[]為是否在棧中的標記數組
int n, m, cnt, scnt, top, tot;
void init()
{
cnt = 0;
scnt = top = tot = 0; //初始化連通分量標號,次序計數器,棧頂指針為0
memset(first, -1, sizeof(first));
memset(DFN, 0, sizeof(DFN)); //結點搜索的次序編號數組為0,同時可以當是否訪問的數組使用
}
void read_graph(int u, int v) //構建鄰接表
{
edge[tot].v = v;
edge[tot].next = first[u];
first[u] = tot++;
}
void Tarjan(int v) //Tarjan算法求有向圖的強連通分量
{
int min, t;
DFN[v] = Low[v] = ++tot; //cnt為時間戳
instack[v] = 1; //標記在棧中
stack[top++] = v; //入棧
for(int e = first[v]; e != -1; e = edge[e].next)
{ //枚舉v的每一條邊
int j = edge[e].v; //v所鄰接的邊
if(!DFN[j])
{ //未被訪問
Tarjan(j); //繼續向下找
if(Low[v] > Low[j]) Low[v] = Low[j]; // 更新結點v所能到達的最小次數層
}
else if(instack[j] && DFN[j] < Low[v])
{ //如果j結點在棧內,
Low[v] = DFN[j];
}
}
if(DFN[v] == Low[v])
{ //如果節點v是強連通分量的根
scnt++; //連通分量標號加1
do
{
t = stack[--top]; //退棧
instack[t] = 0; //標記不在棧中
Belong[t] = scnt; //出棧結點t屬於cnt標號的強連通分量
}while(t != v); //直到將v從棧中退出
}
}
void solve()
{
for(int i = 1; i <= n; i++) //枚舉每個結點,搜索連通分量
if(!DFN[i]) //未被訪問
Tarjan(i); //則找i結點的連通分量
}
int main()
{
while(scanf("%d%d",&n,&m) && (n || m))
{
init();
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
read_graph(u, v);
}
solve(); //求強連通分量
if(scnt == 1) printf("Yes\n"); //只有一個強連通分量,說明此圖各個結點都可達
else printf("No\n");
}
return 0;
}
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define MAXN 10010
#define MAXM 100010
struct Edge
{
int v, next;
}edge[MAXM]; //邊結點數組
int first[MAXN], stack[MAXN], DFN[MAXN], Low[MAXN], Belong[MAXM];
// first[]頭結點數組,stack[]為棧,DFN[]為深搜次序數組,Belong[]為每個結點所對應的強連通分量標號數組
// Low[u]為u結點或者u的子樹結點所能追溯到的最早棧中結點的次序號
int instack[10010]; // instack[]為是否在棧中的標記數組
int n, m, cnt, scnt, top, tot;
void init()
{
cnt = 0;
scnt = top = tot = 0; //初始化連通分量標號,次序計數器,棧頂指針為0
memset(first, -1, sizeof(first));
memset(DFN, 0, sizeof(DFN)); //結點搜索的次序編號數組為0,同時可以當是否訪問的數組使用
}
void read_graph(int u, int v) //構建鄰接表
{
edge[tot].v = v;
edge[tot].next = first[u];
first[u] = tot++;
}
void Tarjan(int v) //Tarjan算法求有向圖的強連通分量
{
int min, t;
DFN[v] = Low[v] = ++tot; //cnt為時間戳
instack[v] = 1; //標記在棧中
stack[top++] = v; //入棧
for(int e = first[v]; e != -1; e = edge[e].next)
{ //枚舉v的每一條邊
int j = edge[e].v; //v所鄰接的邊
if(!DFN[j])
{ //未被訪問
Tarjan(j); //繼續向下找
if(Low[v] > Low[j]) Low[v] = Low[j]; // 更新結點v所能到達的最小次數層
}
else if(instack[j] && DFN[j] < Low[v])
{ //如果j結點在棧內,
Low[v] = DFN[j];
}
}
if(DFN[v] == Low[v])
{ //如果節點v是強連通分量的根
scnt++; //連通分量標號加1
do
{
t = stack[--top]; //退棧
instack[t] = 0; //標記不在棧中
Belong[t] = scnt; //出棧結點t屬於cnt標號的強連通分量
}while(t != v); //直到將v從棧中退出
}
}
void solve()
{
for(int i = 1; i <= n; i++) //枚舉每個結點,搜索連通分量
if(!DFN[i]) //未被訪問
Tarjan(i); //則找i結點的連通分量
}
int main()
{
while(scanf("%d%d",&n,&m) && (n || m))
{
init();
while(m--)
{
int u, v;
scanf("%d%d", &u, &v);
read_graph(u, v);
}
solve(); //求強連通分量
if(scnt == 1) printf("Yes\n"); //只有一個強連通分量,說明此圖各個結點都可達
else printf("No\n");
}
return 0;
}
雙向DFS:
[cpp]
#include<iostream>
#include<cstring>
using namespace std;
const int N=10010;
const int M=100010;
struct Edge
{
int v,next;
}edge1[M],edge2[M];
int vis1[N],vis2[N];
int tot1,tot2;
int first1[N],first2[N];
int num[N];
int cn,ans;
int belong[N];
int a,b,n,m,j;
void init()
{
ans=0;tot1=tot2=0;cn=0;
memset(first1,-1,sizeof(first1));
memset(first2,-1,sizeof(first2));
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
}
void add_edge(int a,int b)
{
tot1++;
edge1[tot1].v=b;
edge1[tot1].next=first1[a];
first1[a]=tot1;
tot2++;
edge2[tot2].v=a;
edge2[tot2].next=first2[b];
first2[b]=tot2;
}
void DFS_1(int v)
{
vis1[v]=1;
for(int i=first1[v];i!=-1;i=edge1[i].next)
{
if(!vis1[edge1[i].v])
DFS_1(edge1[i].v);
}
num[cn++]=v;
}
void DFS_2(int v)
{
vis2[v]=1;
for(int i=first2[v];i!=-1;i=edge2[i].next)
{
if(!vis2[edge2[i].v])
DFS_2(edge2[i].v);
}
}
int main()
{
while(cin>>n>>m,n||m)
{
init();
for(int i=0;i<m;i++)
{
cin>>a>>b;
add_edge(a,b);
}
for(int i=1;i<=n;i++)
{
if(!vis1[i])
{
DFS_1(i);
}
}
for(int i=cn-1;i>=0;i--)
{
if(!vis2[num[i]])
{
DFS_2(num[i]);
ans++;
}
}
if(ans==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
#include<iostream>
#include<cstring>
using namespace std;
const int N=10010;
const int M=100010;
struct Edge
{
int v,next;
}edge1[M],edge2[M];
int vis1[N],vis2[N];
int tot1,tot2;
int first1[N],first2[N];
int num[N];
int cn,ans;
int belong[N];
int a,b,n,m,j;
void init()
{
ans=0;tot1=tot2=0;cn=0;
memset(first1,-1,sizeof(first1));
memset(first2,-1,sizeof(first2));
memset(vis1,0,sizeof(vis1));
memset(vis2,0,sizeof(vis2));
}
void add_edge(int a,int b)
{
tot1++;
edge1[tot1].v=b;
edge1[tot1].next=first1[a];
first1[a]=tot1;
tot2++;
edge2[tot2].v=a;
edge2[tot2].next=first2[b];
first2[b]=tot2;
}
void DFS_1(int v)
{
vis1[v]=1;
for(int i=first1[v];i!=-1;i=edge1[i].next)
{
if(!vis1[edge1[i].v])
DFS_1(edge1[i].v);
}
num[cn++]=v;
}
void DFS_2(int v)
{
vis2[v]=1;
for(int i=first2[v];i!=-1;i=edge2[i].next)
{
if(!vis2[edge2[i].v])
DFS_2(edge2[i].v);
}
}
int main()
{
while(cin>>n>>m,n||m)
{
init();
for(int i=0;i<m;i++)
{
cin>>a>>b;
add_edge(a,b);
}
for(int i=1;i<=n;i++)
{
if(!vis1[i])
{
DFS_1(i);
}
}
for(int i=cn-1;i>=0;i--)
{
if(!vis2[num[i]])
{
DFS_2(num[i]);
ans++;
}
}
if(ans==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}