歐拉回路/路徑學習(求歐拉回路/路徑模板)

參考博客:

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

 

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