判定
首先圖必須是聯通的,用並查集判即可
無向圖歐拉回路:所有點度數都為偶數
無向圖歐拉路徑:兩個點(或0個點)度數為奇數,其余點(或所有點)度數為偶數
有向圖歐拉回路:所有點入度=出度
有向圖歐拉路徑:一個點入度=出度+1,一個點出度=入度+1,其余點(或所有點)入度=出度
查找-Hierholzer算法
//已知存在歐拉路徑,找該路徑
void dfs(int u){//s1~sn中存儲的是歐拉路徑上的點序列
for(int v=1;v<=n;v++){
if(mp[u][v]>0){
mp[u][v]--;
mp[v][u]--;
dfs(v);
}
}
s[temp--]=u;
}
為什么是對的?
因為找歐拉路徑可以通過在圖上的兩個奇數點之間加一條邊轉化為求歐拉回路的問題。通過枚舉可以發現,存在歐拉回路的圖一定是由一些環嵌套而成(可以通過把一些點一分為二近似地看成仙人掌圖)
將一個環看成主環,要一筆畫走完主環,只要在遇到有副環的點u先沿着副環走一圈,再接着沿着主環走就好了
Hierholzer算法就是我們上述思路的一種簡單的實現方法:
我們將開始dfs的點屬於的環視為主環(其實可以有很多種不同的可能性,不一定就是最顯然的那個環),在第一次dfs到無法遞歸下去時,所有被遍歷到的點都在主環上,顯然應該將最后遍歷到的點放在答案數組的最后,然后一邊回溯,如果還是無法遞歸,那接着從后往前存入答案數組,如果可以遞歸說明這個點上有一個副環,沿着副環遞歸下去,也是同樣的最后遍歷到的點放在后面,這樣子就倒着模擬了我們之前的算法。
例題:
The Necklace
https://vjudge.net/contest/347059#problem/C
項鏈的每顆珠子有兩面,每面有一種顏色,項鏈上相鄰兩顆珠子的相鄰面必須是相同的顏色(項鏈是一個環),給定一些珠子和它們的顏色,問能否用盡所有的珠子來組合成一個項鏈?
顏色種類=50
將出現的一種顏色視為圖上的點,一顆珠子的兩個顏色之間有一條邊,顯然在這樣的圖上如果存在一條歐拉回路,那么可以組成項鏈(跑遍所有的邊=用盡所有珠子)。
並查集+Hierholzer算法即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+5;
int a[maxn],b[maxn];
int mp[55][55];
int du[55];
int s[maxn],tot,temp;
void dfs(int u){
for(int v=1;v<=50;v++){
if(mp[u][v]>0){
mp[u][v]--;
mp[v][u]--;
dfs(v);
}
}
s[temp--]=u;
}
int fa[55],vis[55];
int find(int x){
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
int T;
cin>>T;
for(int kase=1;kase<=T;kase++){
int n;
cin>>n;
for(int i=1;i<=50;i++)fa[i]=i;
memset(mp,0,sizeof(mp));
memset(du,0,sizeof(du));
memset(s,0,sizeof(s));
memset(vis,0,sizeof(vis));
tot=0;
for(int i=1;i<=n;i++){
int u,v;
scanf("%d%d",&u,&v);
vis[u]=vis[v]=1;
int fu=find(u),fv=find(v);
fa[fu]=fv;
mp[u][v]++;
mp[v][u]++;
du[u]++;
du[v]++;
tot++;
}
int cnt1=0;
for(int i=1;i<=50;i++){
if(vis[i]&&fa[i]==i)cnt1++;
}
if(cnt1>1){
printf("Case #%d\nsome beads may be lost\n",kase);
continue;
}
int flag=1;
for(int i=1;i<=50;i++){
if(du[i]&1){
flag=0;
break;
}
}
if(!flag){
printf("Case #%d\nsome beads may be lost\n",kase);
if(kase<=T)printf("\n");
continue;
}
temp=tot;
for(int i=1;i<=50;i++){//找一個點
if(vis[i]){
dfs(i);
break;
}
}
printf("Case #%d\n",kase);
for(int i=1;i<=tot;i++){
printf("%d %d\n",s[i],s[i%tot+1]);
}
if(kase<=T)printf("\n");
}
}