欧拉路径与欧拉回路
问题引入
几乎所有人小时候肯定都听说过“一笔画”的游戏,就是给出一张图,用一笔经过所有的边,实际上这就是一个找欧拉路径的过程
基本概念
(以下都是自己瞎诌的,但是感觉也没啥问题)
欧拉路径:不重复的经过每一条边的路径。欧拉回路:起点与终点相同的欧拉路径。首先来探求一下它们存在的条件:
如果是无向图:
那么这个图存在欧拉路径当且仅当:至多有两个点度数为奇数,剩下的点度数都是偶数(两个奇数点当然就是起点和终点了)。这个图存在欧拉回路当且仅当:所有点的度数都是偶数(很好理解,每个点都是一进一出嘛)
如果是有向图:
那么这个图存在欧拉路径当且仅当:至多存在一个点入度是出度
算法流程
那么我们已经掌握了判定条件,只需要证明至少有一种方案找到合法的欧拉路径/欧拉回路就行了。事实上直接 反之是对的就是了
具体地,我们直接以某个点(在某些图中起点是固定的)为起点
3 3
1 3
3 1
1 2
如果按照链式前向星存图,就会先进入 1,2 ,然后发现在 1,2,3,1 ,但显然正确的路径应该是 1,3,1,2 ,出现这种情况的原因就是后面的环没有跑到,所以我们递归求解完之后再塞入栈中,就可以解决这种问题。同时,为了优化复杂度,我们应用类似网络流当前弧优化的手段,走过的边不再走,就能把时间复杂度优化到 毕竟读入也就是这个复杂度了
例题剖析
P7771 【模板】欧拉路径
直接套上板子就好了,由于题目让我们输出字典序最小的方案,所以为了便于实现,我们使用 但是如果你非要用桶排或者基数排序优化到线性也不是不行
Code
#include<bits/stdc++.h>
#define ll long long
#define fi first
#define se second
using namespace std;
const int N=1e6+5;
int n,m,top;
int in[N],out[N],ans[N],now[N];
vector<int> edge[N];
bool vis[N];
void dfs(int x){
for(int i=now[x];i<(int)edge[x].size();i=now[x]){
now[x]=i+1;
int y=edge[x][i];
dfs(y);
}
ans[++top]=x;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
edge[x].push_back(y);
in[y]++;out[x]++;
}
int cnt1=0,cnt2=0,pos1=0;
bool flg=0;
for(int i=1;i<=n;i++){
if(out[i]-in[i]==1){
cnt1++;
pos1=i;
}
else if(in[i]-out[i]==1) cnt2++;
else if(in[i]!=out[i]) flg=1;
}
if(cnt1>1||cnt2>1) flg=1;
if(flg){
printf("No\n");
return 0;
}
for(int i=1;i<=n;i++){
sort(edge[i].begin(),edge[i].end());
}
if(pos1) dfs(pos1);
else dfs(1);
for(int i=top;i>=1;i--) printf("%d ",ans[i]);
return 0;
}
/*
3 3
1 3
3 1
1 2
*/
实战演练
LOJ #10105. 「一本通 3.7 例 1」欧拉回路
这个题相对于上个题来说就更加全面了,不过还是很基础的板子,只要注意细节就好了
Code
这题有一个坑点那就是
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+5,M=4e5+5;
int op,n,m,top,tot=1;
int head[N],ver[M],Nxt[M],edge[M],in[N],out[N],ans[M];
bool vis[M],v[N];
void add(int x,int y,int z){
ver[++tot]=y;
edge[tot]=z;
Nxt[tot]=head[x];
head[x]=tot;
}
void dfs(int x){
v[x]=1;
for(int i=head[x];i;i=head[x]){
head[x]=Nxt[i];
if(op==1){
if(vis[i]||vis[i^1]) continue;
}
else{
if(vis[i]) continue;
}
if(op==1) vis[i]=vis[i^1]=1;
else vis[i]=1;
dfs(ver[i]);
ans[++top]=edge[i];
}
}
int main()
{
scanf("%d",&op);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
in[y]++;out[x]++;
add(x,y,i);
if(op==1) add(y,x,-i);
}
for(int i=1;i<=n;i++){
if(op==1){
if((in[i]+out[i])&1){
printf("NO\n");
return 0;
}
}
else{
if(in[i]!=out[i]){
printf("NO\n");
return 0;
}
}
}
bool flg=0;
for(int i=1;i<=n;i++){
if(v[i]) continue;
if(in[i]+out[i]>0){
if(flg){
printf("NO\n");
return 0;
}
flg=1;
dfs(i);
}
}
printf("YES\n");
for(int i=top;i>=1;i--) printf("%d ",ans[i]);
return 0;
}