kosaraju算法求联通图个数
例题:hdu1269(求整个有向图是否联通 -> 只有一个连通图)
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std ;
const int N = 10005 ;
vector<int> from[N] , to[N] , v ; //用点的数量
int vis[N] , rvis[N];
int cnt ;
void dfs(int x){
if (vis[x]) return ;
vis[x] = 1 ;
for (int i = 0 ; i < to[x].size() ; ++ i) dfs(to[x][i]) ;
v.push_back(x) ;
}
void rdfs(int x){
if (rvis[x]) return ;
rvis[x] = cnt ;
for (int i = 0 ; i < from[x].size() ; ++ i) rdfs(from[x][i]) ;
}
void kosaraju(int n){
cnt = 0 ;
v.clear() ;
memset(vis,0,sizeof(vis)) ;
memset(rvis,0,sizeof(rvis)) ;
for (int i = 1 ; i <= n ; ++ i) dfs(i) ;
for (int i = n-1 ; i >= 0 ; -- i){
if (!rvis[v[i]]){ //按dfs()的进队顺序反着搜索rdfs
++ cnt ; //记录当前连通图的数量
rdfs(v[i]) ;
}
}
}
int main(){
int n , m ;
while(~scanf ("%d%d",&n,&m) ,n != 0 || m != 0){
for (int i = 0 ; i <= n ; ++ i){
from[i].clear() ;
to[i].clear() ;
}
while(m--){
int u , v ;
scanf ("%d%d",&u,&v) ;
to[u].push_back(v) ;
from[v].push_back(u) ;
}
kosaraju(n) ;
if (cnt == 1) printf ("Yes\n") ;
else printf ("No\n") ;
}
return 0 ;
}
tarjan算法实现:
kosaraju是通过两次dfs求,而tarjan是一次dfs,所以tarjan会更加省时
#include <cstdio>
#include <vector>
#include <stack>
#include <cstring>
#include <algorithm>
const int N = 10005 ;
using namespace std ;
int num[N] , low[N] ; //num记录dfs访问的顺序,low记录该点能返回到的最远祖先(low值相同属于一个scc
int dfn , cnt ; //dfn记录dfs递归的顺序 , cnt记录scc的数量
bool vis[N] ;
vector<int> graph[N] ;
stack<int> s ;
void dfs(int u){
num[u] = low[u] = ++ dfn ;
s.push(u) ;
for (int i = 0 ; i < graph[u].size() ; ++ i){
int v = graph[u][i] ;
if (!num[v]){
dfs(v) ;
low[u] = min(low[u],low[v]) ;
}
else if (!vis[v]){ //处理回退边
low[u] = min(low[u],num[v]) ;
}
}
if (low[u] == num[u]){ //scc的祖先
++ cnt ;
while(1){
int v = s.top() ;
s.pop() ;
low[v] = cnt ;
if (u == v) break ; //栈底为scc祖先
}
}
}
void tarjan(int n){
cnt = dfn = 0 ;
memset(num,0,sizeof(num)) ;
memset(low,0,sizeof(low)) ;
memset(vis,false,sizeof(vis)) ;
for (int i = 1 ; i <= n ; ++ i)
if (!num[i])
dfs(i) ;
}
int main(){
int n , m ;
while(~scanf("%d%d",&n,&m)){
if (n == 0 && m == 0) break ;
for (int i = 1 ; i <= n ; ++ i)
graph[i].clear() ;
while(m --){
int u , v ;
scanf ("%d%d",&u,&v) ;
graph[u].push_back(v) ;
}
tarjan(n) ;
if (cnt == 1) printf ("Yes\n");
else printf ("No\n") ;
}
return 0 ;
}
poj1144 network
以下是大佬题解:
以下是代码,emmmmm虽然A了但是总觉的有点似懂非懂,再看看吧。。。。。
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std ;
const int N = 105 ;
int dfn ;
vector<int> graph[N] ;
int num[N] , low[N] ;
bool iscut[N] ; //判断是否是断点
void dfs(int u , int fa){ //当前结点和父结点
num[u] = low[u] = ++ dfn ;
int child ; // 子结点数目
for (int i = 0 ; i < graph[u].size() ; ++ i){
int v = graph[u][i] ;
if (!num[v]){
++ child ;
dfs(v,u) ;
low[u] = min(low[v],low[u]) ; //回溯更新low值
if (low[v] >= num[u] && u != 1) //说明u的支路v上没有回退边连回去,所以u是割点
iscut[u] = true ;
}
else if (num[v] < num[u] && v != fa){ //处理回退边,但该点不是父结点
low[u] = min(low[u] , num[v]) ;
}
}
if (u == 1 && child >= 2) //若为根结点,并且有两个以上的子树
iscut[u] = true ;
}
int main(){
int n ;
while(~scanf("%d",&n) && n!=0){
int u , v ;
for (int i = 1 ; i <= n ; ++ i)
graph[i].clear() ;
while(~scanf("%d",&u) && u != 0){
while(getchar() != '\n'){
scanf ("%d",&v);
graph[u].push_back(v) ;
graph[v].push_back(u) ;
}
}
memset(iscut,false,sizeof(iscut)) ;
memset(num,0,sizeof(num)) ;
memset(low,0,sizeof(low)) ;
dfn = 0 ;
for (int i = 1 ; i <= n ; ++ i)
if (!num[i]) //还没访问过
dfs(i,i) ;
int ans = 0 ;
for (int i = 1 ; i <= n ; ++ i)
if (iscut[i]) ans ++ ;
printf ("%d\n",ans) ;
}
return 0 ;
}