參考博客:
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;
}