欧拉回路/路径学习(求欧拉回路/路径模板)

参考博客:

https://www.cnblogs.com/LMCC1108/p/11306297.html    本校潘武灵/杰巨

https://blog.csdn.net/PacosonSWJTU/article/details/50007847    求回路/路径详解

欧拉回路

如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。

如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。 

具有欧拉回路的图称为欧拉图(简称E图)。具有欧拉路径但不具有欧拉回路的图称为半欧拉图。

 

判断定理 

无向图存在欧拉路径的充要条件

G中奇顶点(连接的边数量为奇数的顶点)的数目等于0或者2。

有向图存在欧拉路径的充要条件

G中顶点入度比出度大1的数目和顶点出度比入度大1的数目相等,并且等于0或者1,其余顶点出度与入度一样。

无向图存在欧拉回路的充要条件

一个无向图存在欧拉回路,当且仅当该图所有顶点度数都为偶数,且该图是连通图

有向图存在欧拉回路的充要条件

一个有向图存在欧拉回路,所有顶点的入度等于出度且该图是连通图

混合图存在欧拉回路条件

要判断一个混合图G(V,E)(既有有向边又有无向边)是欧拉图,方法如下:

假设有一张图有向图G',在不论方向的情况下它与G同构。并且G'包含了G的所有有向边。那么如果存在一个图G'使得G'存在欧拉回路,那么G就存在欧拉回路。

其思路就将混合图转换成有向图判断。实现的时候,我们使用网络流的模型。现任意构造一个G'。用in[i]表示第i个点的入度,out[i]表示第i个点的出度。如果存在一个点k,|out[k]-in[k]|mod 2=1,那么G不存在欧拉回路。接下来则对于所有in[i]>out[i]的点从源点连到i一条容量为(in[i]-out[i])/2的边;对于所有in[i]<out[i]的点从i连到汇点一条容量为(out[i]-in[i])/2的边。如果对于节点U和V,无向边(U,V)∈E,那么U和V之间互相建立容量为1的边。如果此网络的最大流等于

∑|in[i]-out[i]|/2,那么就存在欧拉回路。

例1、Little Sub and Traveling

题解: https://blog.csdn.net/birdmanqin/article/details/90067030 

例2、洛谷P2731 骑马修栅栏 Riding the Fences

题意:给你一个有多重边的无向图,求欧拉路径。

思路:套圈法,回溯时记录路径,dfs即可。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1034;
int mp[510][510],m,n,ans[N],cnt,deg[N];
void dfs(int u)
{
	for(int v=1;v<=n;v++)
		if(mp[u][v])
		{
			mp[u][v]--,mp[v][u]--;
			dfs(v);
		}
	ans[++cnt]=u;
}
int main(void)
{
	bool flag=1;
	int u,v;
	cnt=n=0;
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		n=max(n,max(u,v));
		deg[u]++,deg[v]++;
		mp[u][v]++,mp[v][u]++;
	}
	for(int i=1;i<=n;i++) 
		if(deg[i]&1)
		{
			dfs(i);
			flag=0;
			break;
		}
	if(flag)
	{
		for(int i=1;i<=n;i++) 
			if(deg[i])
			{
				dfs(i);
				break;
			}		
	}
	for(int i=cnt;i>=1;i--)
		printf("%d\n",ans[i]);	
	return 0;	
} 

例3、uoj #117. 欧拉回路 

题意:给出有向图或无向图,求欧拉回路路径的边,含有多重边,自环

代码1(本人垃圾代码,又长又臭):

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 4e5+10; 
struct node
{
	int from,to,id,nxt;
	bool use;
}g[M];
int head[N],cnt,tot;
int t,n,m,u,v,ans[M>>1],deg[N],in[N],out[N],f[N],num;
bool book[N],no;
void add(int u,int v,int id){ g[cnt]=(node){u,v,id,head[u],0},head[u]=cnt++; }
int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); }
void merge(int x,int y)
{
	int fx=getf(x),fy=getf(y);
	if(fx!=fy) f[fx]=fy;
}
void dfs1(int u,int x)
{
	for(int& i=head[u];~i;i=g[i].nxt)//当前弧优化,和网络流中一样 
	{
		if(g[i].use) continue;
		g[i].use=g[i^1].use=1;
		dfs1(g[i].to,g[i].id);
		if(i==-1) break;//不加就错,现在也不明白为啥 
	}
	ans[++tot]=x;
}
void dfs2(int u,int x)
{
	for(int& i=head[u];~i;i=g[i].nxt)
	{
		if(g[i].use) continue;
		g[i].use=1;
		dfs2(g[i].to,g[i].id);
		if(i==-1) break;//不加就错,现在也不明白为啥
	}
	ans[++tot]=x;
}
void solveund()
{
	if(n==1&&m==0)
	{
		puts("YES");
		return ;	
	}	
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		book[u]=book[v]=1;
		merge(u,v);
		add(u,v,i),add(v,u,-i);
		deg[u]++,deg[v]++;
	}
	for(int i=1;i<=n;i++)
		if(deg[i]&1) no=1;
	for(int i=1;i<=n;i++)
		if(book[i]&&getf(i)==i) num++;
	if(num>1) no=1;
	if(no)
	{
		puts("NO");
		return ;
	}
	else puts("YES");
	for(int i=1;i<=n;i++)
		if(book[i])
		{
			dfs1(i,0);
			break;
		} 
	for(int i=tot-1;i>=1;i--)
		printf("%d%c",ans[i]," \n"[i==1]);	
}
void solved()
{
	if(n==1&&m==0)
	{
		puts("YES");
		return ;	
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		book[u]=book[v]=1;
		merge(u,v);
		add(u,v,i);
		out[u]++,in[v]++;
	}
	for(int i=1;i<=n;i++)
		if(in[i]!=out[i]) no=1;
	for(int i=1;i<=n;i++)
		if(book[i]&&getf(i)==i) num++;
	if(num>1) no=1;
	if(no)
	{
		puts("NO");
		return ; 
	}
	else puts("YES");
	for(int i=1;i<=n;i++)
		if(book[i])
		{
			dfs2(i,0);
			break;
		} 
	for(int i=tot-1;i>=1;i--)
		printf("%d%c",ans[i]," \n"[i==1]);	
}
int main(void)
{
	scanf("%d%d%d",&t,&n,&m);
	no=num=tot=0;
	for(int i=1;i<=n;i++)
		head[i]=-1,f[i]=i;
	if(t==1) solveund();
	else solved();
	return 0;
}

 代码2(杰巨代码,精简好用):

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+10;
const int M = 4e5+10; 
struct node
{
	int to,id,nxt;
	bool use;
}g[M];
int head[N],cnt,tot;
int t,n,m,u,v,ans[M>>1],deg[N],in[N],out[N],f[N],num;
int cur[N];
bool book[N],no;
void add(int u,int v,int id){ g[cnt]=(node){v,id,head[u],0},head[u]=cnt++; }
int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); }
void merge(int x,int y)
{
	int fx=getf(x),fy=getf(y);
	if(fx!=fy) f[fx]=fy;
}
void dfs(int u)
{
	for(int& i=head[u];i!=-1;i=g[i].nxt)
	{
		if(g[i].use) continue;
		g[i].use=1;
		if(t==1) g[i^1].use=1;
		int x=i;
		dfs(g[i].to);
		ans[++tot]=g[x].id;
		if(i==-1) break;//不明白,不明白
	}
}
int main(void)
{
	int s=0;
	scanf("%d%d%d",&t,&n,&m);
	no=cnt=num=tot=0;
	for(int i=1;i<=n;i++)
		head[i]=-1,f[i]=i;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		book[u]=book[v]=1;
		merge(u,v);
		if(t==1)
		{
			add(u,v,i),add(v,u,-i);
			deg[u]++,deg[v]++;
		}
		else
		{
			add(u,v,i);
			out[u]++,in[v]++;		
		}
	}
	for(int i=1;i<=n;i++)
	{
		cur[i]=head[i];
		if(t==1)
		{
			if(book[i])
			{
				if(deg[i]&1) no=1;
				else if(!s)  s=i;
				if(getf(i)==i) num++;
			}
		}
		else
		{
			if(book[i])
			{
				if(in[i]!=out[i]) no=1;
				else if(!s) s=i;
				if(getf(i)==i) num++;
			}			
		}
		if(no||num>1)
		{
			no=1;break;			
		}
	}
	if(no) puts("NO");
	else
	{
		puts("YES");
		if(!s) return 0; 
		dfs(s);
		for(int i=tot;i>=1;i--)
			printf("%d%c",ans[i]," \n"[i==1]);
	}
	return 0;
}

例4、 UVA - 10735 Euler Circuit(混合图求欧拉回路)

题意:n个点,m条边。有些边是无向的,有些边是有向的,问是否存在欧拉回路,如果有输出回路。

思路

给出一张混合图(有有向边,也有无向边),判断是否存在欧拉回路。

首先是对图中的无向边随意定一个方向,然后统计每个点的入度(indeg)和出度(outdeg),

如果(indeg - outdeg)是奇数的话,一定不存在欧拉回路;

如果所有点的入度和出度之差都是偶数,那么就开始网络流构图:

1,对于有向边,舍弃;对于无向边,就按照最开始指定的方向建权值为 1 的边;

2,对于入度小于出度的点,从源点连一条到它的边,权值为(outdeg - indeg)/2(假设其和为sum1);

   出度小于入度的点,连一条它到汇点的权值为(indeg - outdeg)/2 的边(假设其和为sum2);

构图完成,如果sum1==sum2满流(求出的最大流值 == 和汇点所有连边的权值之和),那么存在欧拉回路,否则不存在。

^_^路径输出:

就是把所有用到的边都存下来~(原图中的有向边必然要存下来),

但是对于原图中的无向边,我们要根据刚才最大流时每条边中的流量关系来确定其方向:

如果规定方向后,跑出最大流时这条无向边中有流量,说明要这条边正向;否则,将这条边反向

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 110;
const int M = 1e3+10;
const int inf = 0x3f3f3f3f;
struct node{ int to,nxt,ca; }g[M],g1[M];
int head[N],head1[N],cnt,cnt1,in[N],out[N]; 
int cur[N],deep[N],q[N];
int f[N],num;
int n,m,u,v,s,t,sv,sum1,sum2,ans[M],tot;
bool no,book[M];
char op[10];
void Init()
{
	sv=1;
	tot=no=s=0,t=n+1;
	sum1=sum2=num=cnt=cnt1=0;
	for(int i=s;i<=t;i++)
		f[i]=i,head[i]=head1[i]=-1,in[i]=out[i]=0;
}
int getf(int x){ return f[x]==x?x:f[x]=getf(f[x]); }
void add(int u,int v,int ca){ g[cnt]=(node){v,head[u],ca},head[u]=cnt++; }
void add1(int u,int v){ g1[cnt1]=(node){v,head1[u],0},head1[u]=cnt1++; }
void merge(int u,int v)
{
	int fu=getf(u),fv=getf(v);
	if(fu!=fv) f[fu]=fv;
}

bool bfs()
{
	queue<int> q;
	for(int i=s;i<=t;i++) deep[i]=0;	
	deep[s]=1;
	q.push(s);
	while(!q.empty())
	{
		u=q.front(),q.pop();
		if(u==t) return 1;
		for(int i=head[u];i!=-1;i=g[i].nxt)
		{
			v=g[i].to;
			if(!deep[v]&&g[i].ca>0)
			{
				deep[v]=deep[u]+1;
				q.push(v);
			}
		}
	}
	return deep[t]!=0;
}
int dfs(int u,int flow)
{
	if(u==t||!flow) return flow;
	int ans=0,nowflow;
	for(int& i=cur[u];i!=-1;i=g[i].nxt)
	{	
		v=g[i].to;
		if(deep[v]==deep[u]+1&&g[i].ca>0)	
		{
			nowflow=dfs(v,min(flow,g[i].ca));
			if(nowflow)
			{
				ans+=nowflow;
				flow-=nowflow;
				g[i].ca-=nowflow;
				g[i^1].ca+=nowflow;
				if(!flow) break;
			}
		}
	}
	if(!ans) deep[u]=0;
	return ans;
}
int dinic()
{
	int maxflow=0,flow;
	while(bfs())
	{
		for(int i=s;i<=t;i++)
			cur[i]=head[i];
		maxflow+=dfs(s,inf);
	}
	return maxflow;
}
void dfs1(int u)
{
	for(int& i=head1[u];~i;i=g1[i].nxt)
	{
		if(book[i]) continue;
		book[i]=1;
		dfs1(g1[i].to);
		if(i==-1) break;
	}
	ans[++tot]=u;
}
int main(void)
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		Init();		
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d%s",&u,&v,op+1);
			if(op[1]=='U')
				add(u,v,1),add(v,u,0);	
			else
				add1(u,v);
			out[u]++,in[v]++;
			merge(u,v);
		}
		for(int i=1;i<=n;i++)
		{
			if(in[i]==out[i]) continue;
			if((abs(in[i]-out[i])&1)) no=1;
			if(getf(i)==i&&(in[i]||out[i])) sv=i,num++; 
			if(in[i]<out[i])
			{
				add(s,i,(out[i]-in[i])/2),add(i,s,0),sum1+=(out[i]-in[i])/2;	
			} 
			else if(in[i]>out[i])
			{
				add(i,t,(in[i]-out[i])/2),add(t,i,0),sum2+=(in[i]-out[i])/2;
			} 
			if(num>1||no)
			{
				no=1;break;
			}
		}	
		if(no||sum1!=sum2)
		{
			puts("No euler circuit exist\n");
			continue;
		}
		if(dinic()!=sum2)
		{
			no=1;	
		} 
		if(no)
		{
			puts("No euler circuit exist\n");
			continue;
		}
		for(u=1;u<=n;u++)
			for(int i=head[u];~i;i=g[i].nxt)
			{
				if(g[i].to==t||g[i].to==s) continue;
				if(g[i].ca)
					add1(u,g[i].to);
			}
		memset(book,0,sizeof(book));
		dfs1(sv);
		for(int i=tot;i>=1;i--)
			printf("%d%c",ans[i]," \n"[i==1]);
		puts("");		
	}	
	
	return 0;	
} 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章