在欧拉中经常会用到联通块 而这里的联通块并不是用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); }