欧拉路径&欧拉回路
判定
无向图
- 欧拉路径:可以一笔画,充要条件是度数为奇数的点的个数为0或2
 
- 欧拉回路:欧拉路劲构成一个环,充要条件是全部是偶点
 
有向图
- 欧拉路径:起点出度比入度大1,终点入度比出度大1,其他全部是偶点
 
- 欧拉回路:每个点出入度相同(至多存在两个点出入度不同,且这两个点满足起点出度比入度大1,终点入度比出度大1),且存在一系列环可以覆盖原图
 
求路径:Fleury算法
求图的欧拉回路的算法:Fleury算法
对于无向图和有向图,求路径的方法是一样的,简单描述为:在任意可行点处进行dfs,过程中找到一条回路之后从终点开始往回删除dfs过程中的所有边(注意,这里如果是无向图的话,需要删双向边,那么可以通过异或操作完成)如果删掉这些边之后的点还能够通往别的路径的话,就继续dfs通往,直到递归结束,此时,倒着输出所有遍历的节点就行了(无向图正着倒着都一样),以上过程都可以通过dfs的特性(栈)一气呵成
伪代码
 | void dfs(int u){     for(遍历所有u的出边){         if(出边没被删){             删掉这条边(无向图需要删双向,可以用^运算实现)             dfs(这条边所连的点)         }     }     把u压入栈中 }
 
  | 
 
具体代码见下方例题2、3
模板题
判断一个无向图是否有欧拉路径
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | #include<bits/stdc++.h> using namespace std; int n,m; vector<int> gg[50005]; int main(){ 	cin>>n>>m; 	int u,v; 	for(int i=1;i<=m;i++){ 		cin>>u>>v; 		gg[u].push_back(v); 		gg[v].push_back(u); 	} 	int ans=0; 	for(int i=1;i<=n;i++){ 		int temp=0; 		int Size=gg[i].size(); 		for(int i=0;i<Size;i++){ 			temp++; 		} 		if(temp%2!=0){ 			ans++; 		} 	} 	if(ans==0||ans==2) cout<<"Full"<<endl; 	else cout<<"Part"<<endl; 	return 0; }
 
  | 
 
 
重题POJ 2230 Watchcow(但此题用stack会RE)
求一个无向图的任意一条欧拉回路
应用Fleury算法,代码如下
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
   | #include<bits/stdc++.h> using namespace std; int n,m; struct Edge{ 	int u,v,w,next; 	bool f; }e[50005]; int cnt,head[50005]; void init(){ 	memset(head,-1,sizeof(head)); 	cnt=0; } inline void addedge(int u,int v,int w=0){ 	e[cnt]=Edge{u,v,w,head[u],false}; 	head[u]=cnt++; } stack<int> sta; void dfs(int u){ 	for(int i=head[u];~i;i=e[i].next){ 		int v=e[i].v; 		if(!e[i].f){ 			e[i].f=e[i^1].f=1; 			dfs(v); 		} 	} 	sta.push(u); } int main(){ 	cin>>n>>m; 	int u,v;init(); 	for(int i=1;i<=m;i++){ 		cin>>u>>v; 		addedge(u,v); 		addedge(v,u); 	} 	 	dfs(1); 	while(sta.size()){ 		cout<<sta.top()<<" "; 		sta.pop();  	} 	return 0; }
 
  | 
 
 
题意:
给出一个正整数n,求一个长度为$2^n$的01环,使得该环的每n位恰好构成$0~2^{n-1}$,例如:n=3时,答案为00010111
题解:
很有意思的一道题,考虑将其转化为图论题:例如,对于n=3的情况,抽取其中任意的长度为3的子串,如:101,将其拆分为10、01,那么就可以建图从(10)->(01),由此,从$0~2^{n-1}-1$只要有一个数的后n-1位和另一个数的前n-1位相同,那么就可以从前一个数连一条边到后一个数,显然共有$2^n$条边,跑一次欧拉回路,用栈记录路径,最后输出时一定要倒着输出(栈&1)的值
AC代码:
code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
   | #include<bits/stdc++.h> using namespace std; int n,m; struct Edge{ 	int u,v,w,next; 	bool f; }e[50005]; int cnt,head[50005]; void init(){ 	memset(head,-1,sizeof(head)); 	cnt=0; } inline void addedge(int u,int v,int w=0){ 	e[cnt]=Edge{u,v,w,head[u],false}; 	head[u]=cnt++; } stack<int> sta; void dfs(int u){ 	for(int i=head[u];~i;i=e[i].next){ 		int v=e[i].v; 		if(!e[i].f){ 			e[i].f=1;  			dfs(v); 		} 	} 	sta.push(u); } int main(){ 	cin>>n; 	if(n==1){ 		cout<<"01"<<endl; 		return 0; 	} 	init(); 	int k=(1<<n-2)-1; 	for(int i=0;i<1<<n-1;i++){ 		int giao=i&k; 		for(int j=0;j<1<<n-1;j++){ 			if(giao==j>>1){ 				addedge(i,j); 			} 		} 	} 	 	dfs(0); 	sta.pop(); 	while(sta.size()){ 		cout<<(sta.top()&1); 		sta.pop(); 	} 	return 0; }
 
  |