浅谈欧拉路径,欧拉回路,以及有向图欧拉回路的计数

浅谈欧拉路径,欧拉回路

引入

前有哈密顿路径,表示经过每个点恰好一次,现有欧拉路径,表示经过每条边恰好一次。

许多题目重要的是建模,往往最浅的建模就是点之间的连边,表示可以到达。如果说需要满足到达每个点一次,这就变成了 N P C \tt NPC NPC 问题。但是我们往往可以将一个信息拆分成若干个信息,变成边之间的关系,这样就有多项式复杂度的解法,同样这个是可以求方案的。

本篇会向读者介绍欧拉路径和欧拉回路以及其判定方法,还有常用的套路。


欧拉图

具有欧拉回路的图叫欧拉图,如果只有欧拉路径就叫做半欧拉图。


欧拉路径

定义:

经过每条边恰好一次,起点和终点不一定相同。

无向图

每个点的度数都是偶数,或者只有两个节点度数是奇数。

有向图

设一个点的权值 v i v_i vi 表示其出度减去入度。

那么存在的欧拉路径的条件是 v i = 0 v_i = 0 vi=0 或者同时存在一个 v i = 1 , v j = − 1 , i ≠ j v_i = 1, v_j = -1, i \ne j vi=1,vj=1,i=j


欧拉回路

定义

经过每条边恰好一次,起点和终点相同。

无向图

每个点的度数都是偶数。

有向图

设一个点的权值 v i v_i vi 表示其出度减去入度。

那么存在的欧拉路径的条件是 v i = 0 v_i = 0 vi=0


具体实现

如果说对于一条路径起点和终点是不同的,作为开始的节点需要是出度较大的节点。

我们考虑直接进行暴力 d f s \tt dfs dfs,每次删除一条边。在遍历完其相邻的所有点之后将当前点加入答案。

复杂度是 O ( n + m ) O(n + m) O(n+m) 的。

void dfs(int p) {
   
    while(!vc[p].empty()) {
   
        int v = vc[p].back(); vc[p].pop_back();
        dfs(v);
    }
    ans[++ ed] = p;
}   

为什么要最后加入当前点呢?

如果说出现环的情况,而且我们对于当前点 u u u 并不是第一次遍历环,那么直接记录答案显然是不行的。

但是我们可以先走这个环,再走链。这种时候就可以考虑最后加入点,这样就是倒着走,环并不会影响答案。

读者可以尝试画画图,因为笔者个人博客从来不放图,请见谅。

因为这样我们的答案是反着的,所以我们可以翻转一下数组。

之后的答案都指翻转数组之后的。


套路

字典序要求
  • 字典序最小:贪心走最小的点即可。
  • 字典序最大:贪心走最大的点即可。

拆点成边
例题1

求一个最短的字符串,使得其包含所有的 n n n k k k 进制数。

最好的方法就是考虑每次增加一个字符,但是显然直接来做是不方便的。

可能会考虑对于每一个 k k k 进制数,其能到达的数连边加边权,边权是需要的字符数。

这样转换只能用 2 x 2^x 2x 指数级暴力,状压来写。

我们考虑将每个 n n n 位数,拆分成两个 n − 1 n - 1 n1 位的数。 ( a 1 a 2 a 3 a 4 a 5 … a n − 1 ) k → ( a 2 a 3 a 4 a 5 a 6 … a n ) k (a_1a_2a_3a_4a_5\dots a_{n - 1})_k \to (a_2 a_3 a_4 a_5 a_6 \dots a_n)_k (a1a2a3a4a5an1)k(a2a3a4a5a6an)k 进行连边。

这样拆出来的每条边都需要经过恰好一次,每次正好是增加一个字符,可以保证答案最小。

如何保证有解?

显然对于最终的一个字符串肯定能拆分成若干个上述形式的 n − 1 n - 1 n

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值