發現這個小東西雖然很簡單但是考一次掛一次
A.定義
歐拉路:圖中任意一個點開始到圖中任意一個點結束,且通過的每條邊只被通過一次的路徑。
歐拉回路:同上,不過起點與終點相同。
B.判定
這里只以歐拉路為例。
無向圖:對於一張無向圖,當且僅當圖聯通且奇點數為0或2時,存在一條能遍歷整張圖的歐拉路。如果奇點數為2,那么這兩個點應當作為歐拉路的起點和終點,否則任意一點都可作為歐拉路的起點和終點。
有向圖:對於一張有向圖,當且僅當圖聯通且有0個或2個點的入度不等於出度時,存在一條能遍歷整張圖的歐拉路。如果有2個點,那么這兩個點當中一個必須入度=出度-1作為起點,另一個必須出度=入度-1作為終點,否則任意一點都可作為歐拉路的起點和終點。
C.算法
一般使用Hierholzer算法解決歐拉路問題,時間復雜度為$O(n+m)$。
首先來看優化前的$O(nm)$版本:
void dfs(int x) { for(int i=head[x];i;i=nxt[i]) { if(vis[i])continue; int y=to[i]; vis[i]=vis[i^1]=1;//The original edge_num should be 1. dfs(y); } st[++top]=x; }
很弱智是吧?
可能第一次看會覺得它不能保證形成包括所有邊的方案,然而最后把點入棧的過程其實就是“拼湊”多條子歐拉路的過程。
所以只要這個圖合法,最后一定可以得到一個經過所有邊的方案。把棧內元素倒序輸出即可。
不理解可以畫個圖手玩一下。
這種暴力算法的問題在於,雖然我們已經標記了走過的邊,但還是到每個點時會從第一條邊開始遍歷,即使已經有一堆邊不能走了。
怎么辦呢?還記得dinic的當前弧優化嗎?
沒錯,加上它就好了。
void dfs(int x) { for(int i=head[x];i;i=nxt[i]) { if(vis[i])continue; int y=to[i]; vis[i]=vis[i^1]=1;//The original edge_num should be 1. head[x]=i; dfs(y); i=head[x]; } st[++top]=x; }
那么我們就可以在$O(n+m)$的時間復雜度內求出一條歐拉路辣!
還有一件事……
這個算法的遞歸層數是$O(m)$級別的,就算沒爆棧也會使遞歸過程奇慢無比。
所以大概還要手寫系統棧用循環模擬一下:
int syst[N*10],systop; void euler() { systop=0; syst[++systop]=0; while(systop>0) { int x=syst[systop],i=head[x]; while(i&&v[i])i=nxt[i]; if(i) { syst[++systop]=to[i]; v[i]=v[i^1]=1; head[x]=nxt[i]; } else systop--,st[++top]=x; } }
完結撒花!