POJ2186
题解:
- 找出一个强连通分量,判断其它边是否全部直接或者间接指向它
- 利用Kosaraju找强连通分量
- 如果找到了强连通分量A,下一个强连通分量B,在原有向图中,一定是A指向B。
- 所以最后一个强连通分量必定是没有指向任何强连通分量的,也就是有可能成为被崇拜的。
- 所以我们在最后一个分量开始在反向图中跑dfs2,如果每一个点都被遍历到了,说明所有的点都指向了它。
- 还有一种想法是判断出度的个数,如果只有一个连通分量的出度为0,那么满足题意。在Tarjan算法中有写到。
代码:Kosaraju
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int const N = 10000 + 10;
int n,m,scc,cnt;
int vis[N],sccno[N];
vector<int>G1[N],G2[N],s;
void dfs(int u){
vis[u] = true;
for(int i=0;i<G1[u].size();i++){
int v = G1[u][i];
if(vis[v]) continue;
vis[v] = true;
dfs(v);
}
s.push_back(u); //反向记录拓扑序列
}
void dfs2(int u){
vis[u] = true;
sccno[u] = scc; //连通分块的编号
for(int i=0;i<G2[u].size();i++){
int v = G2[u][i];
if(vis[v]) continue;
dfs2(v);
}
}
int main(){
while(~scanf("%d%d",&n,&m)){
s.clear();
for(int i=0;i<=n;i++) G1[i].clear(), G2[i].clear();
for(int i=0;i<m;i++){
int from,to;
scanf("%d%d",&from,&to);
G1[from].push_back(to);
G2[to].push_back(from);
}
memset(vis,false,sizeof(vis));
for(int i=1;i<=n;i++)
if(!vis[i]) dfs(i);
memset(vis,false,sizeof(vis));
for(int i=n-1;i>=0;i--){
if(vis[s[i]]) continue;
scc++; dfs2(s[i]);
}
int u;
for(int i=1;i<=n;i++){
if(sccno[i] == scc){ //找最后一个联通分块
cnt++;
u = i;
}
}
memset(vis,false,sizeof(vis));
dfs2(u);
for(int i=1;i<=n;i++)
if(!vis[i]){
cnt = 0;
break;
}
cout<<cnt<<endl;
}
return 0;
}
Tarjan
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <stack>
#include <vector>
using namespace std;
int const N = 10000 + 10;
int n,m,scc,cnt;
int vis[N],sccno[N],lowlink[N],pre[N],sum[N],out[N];
vector<int>G[N];
stack<int>st;
void dfs(int u){
lowlink[u] = pre[u] = ++cnt;
st.push(u);
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(!pre[v]){
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}else if(!sccno[v]){
lowlink[u] = min(lowlink[u],pre[v]);
}
}
if(lowlink[u] == pre[u]){
scc++;
while(1){
int x = st.top(); st.pop();
sum[scc]++;
sccno[x] = scc;
if(x == u) break;
}
}
}
void Tarjan(){
cnt = scc = 0;
memset(sum,0,sizeof(sum));
memset(pre,0,sizeof(pre));
memset(sccno,0,sizeof(sccno));
for(int i=1;i<=n;i++)
if(!pre[i]) dfs(i);
}
void solve(){
memset(out,0,sizeof(out));
for(int i=1;i<=n;i++)
for(int j=0;j<G[i].size();j++){
int v = G[i][j];
if(sccno[i] != sccno[v]) out[sccno[i]]++;
}
int ans = 0;
for(int i=1;i<=scc;i++){
if(!out[i]){
if(ans > 0){ //找到两个
printf("0\n");
return;
}
ans = sum[i];
}
}
printf("%d\n",ans);
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) G[i].clear();
for(int i=0;i<m;i++){
int from,to;
scanf("%d%d",&from,&to);
G[from].push_back(to);
}
Tarjan();
solve();
}
return 0;
}