在歐拉中經常會用到聯通塊 而這里的聯通塊並不是用tarjan來求
而是用並查集 find(i) 就能找到i所在的聯通塊的編號
遍歷每一個點 如果是j聯通塊的就進行處理 既能實現對某個聯通塊里點的處理
遍歷每個點的find(i)放到set里 那set.size() 就是聯通塊的個數
看到“每條路只能走一次”,“所有的路”,這樣類似的字眼,就要想到歐拉路
看是無向邊 還是有向邊
無向邊 要考慮度數 有向邊對應就是入度和出度 注意在有向圖中 奇點是成對出現的
如果題目中沒有保證是否是一個連通圖,那么可以用並查集或者dfs維護,來判斷是否為連通圖,
並查集維護時 如果存在自環的情況,則在輸入的時候用vis標記一下這個點是否出現過
先復習一下歐拉路的基本概念 https://blog.csdn.net/NOIAu/article/details/78203851
歐拉回路:是指所有的邊都只經過一次且僅一次,並且要走回到出發點的一條路徑
歐拉路徑:表示一條不需要回到出發點,但是必須經過所有的邊且都只經過一次的路徑
無向圖存在歐拉回路的充要條件是所有的點的度數均為偶數
無向圖存在歐拉路徑的充要條件是度數為奇數的點的數量為0個或者2個
有向圖存在歐拉回路的充要條件是所有的點的出度均等於入度
有向圖存在歐拉路徑的充要條件是:
①:歐拉回路的情況
②:所有點中出度比入度大1的點有一個,入度比出度大1的點有一個,不允許有大幾個的情況
歐拉回路套路:
判斷是否只有一個連通塊
無向邊 還是有向邊 還是混合邊
統計度數,同時並查集維護連通塊
如果是混合邊
1、定向建邊
2、判斷度數建邊
3、Dinic跑一遍,如果s的流出量 == t的流入量,那ok是歐拉回路
如果讓輸出路徑
那就用fleury 手動開個棧 如果dfs不大行 那就用中轉棧再模擬dfs
還做不了 那就。。。。放棄吧。。。
混合圖建邊:
1、另x = |入度-出度|/2;對於不同的點有不同的x值,這個x值代表它們在鄰接表中相應調整x條就能讓出度等於入度。
2、以把圖中的點轉換為一個二分圖,每個點的x值就是它們的點權。
3、置源點S向所有出度>入度的點連邊;設置匯點T,所有入度大於出度的點連邊,將各自的點權轉換為邊權。
(定向建邊時,只建立雙向邊,遍歷每一個點,處理度數,而源點的流想要流向匯點就一定需要流過雙向邊在網絡流圖中建立的邊)
4、最后將原圖中所有暫時定向的無向邊加上一個1的容量,方向不變,而有向邊不能改變方向,不需連邊。
可以發現,從源點S出發的一個單位流將會一個“無向邊”的容量變為0,使得兩端的點權各自減1,其實這就是在模擬一次對無向邊方向的調整。當把圖建好后,依靠最大流性質可以最大可能地無沖突調整邊的方向,並最終使得每個點的點容量都達到滿流。
最后,還要對那些圖中出度等於入度的點做適當分析,它們作為一個“中間點”,由於流平衡性質,不會留下任何流量值,對於那些真正需要調整的點不會帶來任何影響。
最后,如何得到答案?那就是檢查從源點出發的每條邊是否都滿流,如果有一條邊沒有滿流,說明有一個點沒有調整到入度等於出度,於是整個圖不存在歐拉回路。
如果求的是混合圖歐拉路徑 則建邊時要從t到s建一條容量為1的邊 來保證容量平衡
fleury:
離散課本上的emm。。
吶 先是
無向圖的代碼:
歐拉回路 和 歐拉路徑
#include <bits/stdc++.h> using namespace std; const int maxn = 50, INF = 0x7fffffff; int n, m, s, top; int w[maxn][maxn], stk[maxn],deg[maxn]; void dfs(int u) { for(int i = 1; i <= n; i++) { if(w[u][i]) { w[u][i]--; w[i][u]--; dfs(i); } } stk[top++] = u; } void fleury() { top = 0; dfs(s); } void print() { for(int i = top - 1; i >= 0; i--) { cout << stk[i] << " "; } cout << endl; } int main() { int u, v; cin >> n >> m; for(int i = 0; i < m; i++) { cin >> u >> v; w[u][v]++; w[v][u]++; deg[u]++; deg[v]++; } s = 1; int ans = 0; for(int i = 1; i <= n; i++) { if(deg[i] & 1) { ans++; s = i; } } if(!ans || ans == 2) //歐拉回路 和 歐拉路徑 { fleury(); print(); } else { cout << "No" << endl; } return 0; }
有向圖
#include <bits/stdc++.h> #define mem(a, b) memset(a, b, sizeof(a)) using namespace std; const int maxn = 10010, INF = 0x7fffffff; int head[maxn], in[maxn], out[maxn], stk[maxn]; int n, m, s, cnt, top; struct edge { int u, v, flag, next; }Edge[maxn]; void add(int u, int v, int flag) { Edge[cnt].u = u; Edge[cnt].v = v; Edge[cnt].flag = flag; Edge[cnt].next = head[u]; head[u] = cnt++; } void dfs(int u) { for(int i = head[u]; i != -1; i = Edge[i].next) { if(!Edge[i].flag) { Edge[i].flag = 1; dfs(Edge[i].v); } } stk[top++] = u; } void fleury() { top = 0; dfs(s); } void print() { for(int i = top - 1; i >= 0; i--) { cout << stk[i] << " "; } cout << endl; } void init() { mem(head, -1); cnt = 0; mem(in, 0); mem(out, 0); } int main() { mem(head, -1); int u, v; cnt = 0; cin >> n >> m; for(int i = 0; i < n; i++) { cin >> u >> v; add(u, v, 0); in[v]++; out[u]++; } int ans = 0; for(int i = 1; i <= n; i++) { if(in[i] != out[i]) ans++; if(in[i] < out[i]) s = i; } if(!ans || ans == 2) fleury(), print(); else cout << "No" << endl; return 0; }
如果是混合圖fleury
先把有向邊放到Edgeli
跑完網絡流 遍歷每條正向邊 如果Node[i].c == 0 則 add(Node[i],v, Node[i].u)
如果 Node[i].c != 0 則 add(Node[i].u, Node[i].v)
fleury 按照 有向圖的進行即可
此部分代碼如下
for(int i = 0; i < cnt; i++) { if(!Edge[i].ff || Edge[i].u == s || Edge[i].v == t) continue; if(Edge[i].c == 0) add2(Edge[i].v, Edge[i].u); else add2(Edge[i].u, Edge[i].v); }