参考博客:
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,那么就存在欧拉回路。
题解: 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;
}
题意:给出有向图或无向图,求欧拉回路路径的边,含有多重边,自环。
代码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;
}