图论

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 ; 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值