1.以任意一个点为源点,进行dfs,并将记录经过点的时间戳,时间戳逐渐增加。
2.进行dfs后,将图中的边的方向反向。寻找时间戳最小的点为源点(就是上面源点)进行dfs。这时,它所能达到的点集就是一个连通分量。并记录搜索过的点
3.在没有搜索过的点中以时间戳最小的点为源点,继续dfs,搜索结果同上
4.不断重复3,直到所有点都搜索过。
这个算法的意思就是如果某个点在边反向之前能到达,在边反向之后也能到达,那就说明这两个点是强连通的。
我是照着挑战程序设计竞赛抄的模板。书上说的是“给每个点标号,随着dfs进行标号变小”的意思。这里的标号应当是指其在经过dfs后,每个点在vs容器中的位置。每次从标号较大的点进行搜索,这里标号较大的点肯定是某连通分量树的根节点。我尝试了下相反的方法,即随着dfs进行标号变大的方法,结果证明我的想法是错的。如1<->2<-3。这时结果就是错的。代码就是我下面的第二个代码。
#include<vector>
#include<iostream>
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10000+10
int v;
vector<int>G[N];
vector<int>rG[N];
vector<int>vs;
bool used[N];
int cmp[N];
int n, m;
void add(int from, int to){
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v){
used[v] = true;
for (int i = 0; i < G[v].size(); i++){
if (!used[G[v][i]])dfs(G[v][i]);
}
vs.push_back(v);
}
void rdfs(int v, int k){
used[v] = true;
cmp[v] = k;
for (int i = 0; i < rG[v].size(); i++){
if (!used[rG[v][i]])rdfs(rG[v][i], k);
}
}
int scc(){
mem(used, 0);
vs.clear();
for (int v = 1; v <= n; v++){
if (!used[v])
dfs(v);
}
mem(used, 0);
int k = 0;
for (int i = vs.size() - 1; i >= 0; i--){
if (!used[vs[i]])
rdfs(vs[i], k++);
}
return k;
}
int main(){
while (cin >> n >> m){
if (!n&&!m)break;
for (int i = 0; i <= n; i++){
G[i].clear();
rG[i].clear();
}
while (m--){
int a, b; cin >> a >> b;
add(a, b);
}
if (scc() == 1)cout << "Yes" << endl;
else cout << "No" << endl;
}
}
下面是错的,我也在思考为啥
#include<vector>
#include<iostream>
using namespace std;
#define mem(arr,a) memset(arr,a,sizeof(arr))
#define N 10000+10
int v;
vector<int>G[N];
vector<int>rG[N];
vector<int>vs;
bool used[N];
int cmp[N];
int n, m;
void add(int from, int to){
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v){
used[v] = true;
vs.push_back(v);
for (int i = 0; i < G[v].size(); i++){
if (!used[G[v][i]])dfs(G[v][i]);
}
}
void rdfs(int v, int k){
used[v] = true;
cmp[v] = k;
for (int i = 0; i < rG[v].size(); i++){
if (!used[rG[v][i]])rdfs(rG[v][i], k);
}
}
int scc(){
mem(used, 0);
vs.clear();
for (int v = 1; v <= n; v++){
if (!used[v])
dfs(v);
}
mem(used, 0);
int k = 0;
for (int i = 0; i < vs.size();i++)
{
if (!used[vs[i]])
rdfs(vs[i], k++);
}
return k;
}
int main(){
while (cin >> n >> m){
if (!n&&!m)break;
for (int i = 0; i <= n; i++){
G[i].clear();
rG[i].clear();
}
while (m--){
int a, b; cin >> a >> b;
add(a, b);
}
if (scc() == 1)cout << "Yes" << endl;
else cout << "No" << endl;
}
}