欧拉路径
概念
- 欧拉路径:从某一点出发经过一条不间断的路径,这条路径刚好访问整个图的所有遍一次且仅一次。
- 这个图可以是有向图/无向图。
- 一个图可以有多个欧拉路径
- 欧拉回路:起点和终点为同一个点的欧拉路径。
- 一个图可以有多个欧拉回路
- 欧拉图:具有欧拉回路的图
- 半欧拉图:具有欧拉路径但是不具有欧拉回路的图
存在条件
- 所有有向边改为无向边后图是否连通:可以用dfs或并查集来找
- 欧拉图
- 无向图:所有顶点度数都是偶数
- 有向图:图的所有顶点都满足“入度=出度”
- 半欧拉图
- 无向图:有且仅有两个点的度数是奇数,其余的点度数都为偶数
- 有向图:有且仅有一个点“入度=出度+1” 并且 有且仅有一个点“出度=入度+1”,其余的点“入度=出度”
半欧拉图的起点 & 终点
- 无向图:两个度数为奇数的一个 --> 另一个
- 有向图:“出度=入度+1” --> “入度=出度+1”
欧拉路径算法
- 判断给定图是否是欧拉图/半欧拉图(如果没有连通的限制还要判定是否是连通图)
- 确定路径起点
- 欧拉图任意
- 半欧拉图见上
- 答案栈answer存储途径节点
- 对图进行dfs,节点允许重复经过,边不允许。每访问一条边就删掉,然后对这条边另一端的点进行遍历。每一个节点完成遍历且没有相连边时,将这个节点加入answer
- 遍历结束,answer依次弹出,得到的就是欧拉路径/欧拉回路的节点访问顺序
注意点
不管是answer存边还是存点,都要等dfs完了再存,然后倒序输出。
代码
luogu的模版题:【模版】欧拉路径
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll inf=0x3f3f3f3f3f3f3f3f;
vector<ll> e[100005]; //邻接表存图
ll nxt[100005]; //从i出发下一个要访问的边的序号为nxt[i](以保证每条边只访问一次)
ll in[100005],out[100005];
stack<ll> ans; //答案栈
ll n,m;
void dfs(ll x);
void solve();
void dfs(ll x){
for(ll i=nxt[x];i<e[x].size();i=nxt[x]){
nxt[x]++;
dfs(e[x][i]);
}
ans.push(x);
}
void solve(){
cin>>n>>m;
for(ll i=1,x,y;i<=m;i++){
cin>>x>>y;
e[x].push_back(y);
++out[x],++in[y];
}
//判断是否有欧拉路径
ll start=0,start_x=1,end=0,end_x=0,cnt=0;
for(ll i=1;i<=n;i++){
if(in[i]+1==out[i]){
++start;
start_x=i;
} else if(out[i]+1==in[i]){
++end;
end_x=i;
} else if(in[i]==out[i]){
++cnt;
}
}
if(!(start==1 && end==1 && cnt==n-2) && !(cnt==n)){
//!(start==1 && end==1 && cnt==n-2) 不是半欧拉图
//!(cnt==n) 不是欧拉图
//无欧拉路径
cout<<"No\n";
return ;
}
//有欧拉路径
for(ll i=1;i<=n;i++){
sort(e[i].begin(),e[i].end());
}
dfs(start_x);
while(!ans.empty()){
cout<<ans.top()<<' ';
ans.pop();
}
cout<<'\n';
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
ll t=1;
// cin>>t;
while(t--){
solve();
}
return 0;
}
引用
BV1Y341177eW
【模版】欧拉路径题解@Marsrayd的博客

浙公网安备 33010602011771号