D165【模板】有向图 欧拉路径 欧拉回路 P7771 欧拉路径
D165【模板】有向图 欧拉路径 欧拉回路 P7771 欧拉路径_哔哩哔哩_bilibili
每条边恰好经过一次的路径称为欧拉路径,每条边恰好经过一次又回到起点的路径称为欧拉回路,通称一笔画的路径

有向图的欧拉回路判定:所有点的入度等于出度,如图 1
有向图的欧拉路径判定:有且仅有两个点,入度不等于出度。出度比入度多1的点做起点;入度比出度多1点做终点,如图 2
图 3 存在 4 个点不满足条件,不存在欧拉路径
求有向图字典序最小的欧拉路径。$n\le 10^5,\;m\le 2\times 10^5$
思路
点数很多,不能用邻接矩阵存图,考虑用 vector 邻接表 存图,怎样保证字典序最小呢?
存图后,对每个点的邻接点排序,如图

排除无解的情况,从起点开始搜索,怎样删除边呢?
不用显示删除,开一个全局数组 p[x] 记录 x 点已经处理到第几条出边,下次再搜到 x 点,直接从下一条边搜就行了
如图,$1→2→3→1→3→4→1→4$,搜索路径是一条链,最后回溯到每个点时,边都用完了
对所有边排序的时间复杂度 $O(mlogm)$
// 欧拉路径 欧拉回路 O(mlogm) #include<bits/stdc++.h> using namespace std; const int N=100005; int n,m,rd[N],cd[N]; stack<int> path; vector<int> e[N]; //邻接表 int p[N]; //p[x]表示点x当前处理到第几条出边,初始值为0,相当于全局指针 void dfs(int x){ for(int i=p[x]; i<e[x].size(); i=p[x]){ p[x]=i+1; //全局指针,指向下一条边 dfs(e[x][i]); } path.push(x); } int main(){ scanf("%d%d",&n,&m); for(int i=1,u,v;i<=m;i++){ scanf("%d%d",&u,&v); e[u].push_back(v); //邻接表连边 cd[u]++; rd[v]++; //累加出度,入度 } for(int i=1;i<=n;i++) sort(e[i].begin(),e[i].end()); //按邻接点排序 int cs=0,rs=0; bool ok=true; //存在欧拉路 for(int i=1; i<=n; i++){ if(rd[i]!=cd[i]){ if(cd[i]-rd[i]==1) cs++; //出度多1的点数 else if(rd[i]-cd[i]==1) rs++; //入度多1的点数 else ok=false; //度差>1,一定不存在欧拉路 } } if(!(!cs&&!rs || cs==1&&rs==1)) ok=false; //非环非路一定不存在 if(!ok) return puts("No"),0; int start=1; while(!cd[start]) ++start; //找环的起点 for(int i=1; i<=n; i++) if(cd[i]-rd[i]==1){start=i; break;} //找路的起点 dfs(start); //搜索欧拉路 while(!path.empty())printf("%d ",path.top()),path.pop(); return 0; }
// 欧拉路径 欧拉回路 O(mlogm) #include<bits/stdc++.h> using namespace std; const int N=100005,M=200005; int n,m,cd[N],rd[N]; stack<int> path; struct E{int x,y;}e[M]; //边 int to[M],ne[M],h[N],idx; //链式前向星 void add(int x,int y){ to[++idx]=y;ne[idx]=h[x];h[x]=idx; } void dfs(int x){ for(int i=h[x]; i; i=h[x]){ h[x]=ne[i]; //全局指针,指向下一条边 dfs(to[i]); } path.push(x); } int main(){ scanf("%d%d",&n,&m); for(int i=1; i<=m; ++i){ scanf("%d%d",&e[i].x,&e[i].y); ++cd[e[i].x]; ++rd[e[i].y]; //累加出度,入度 } sort(e+1,e+m+1,[&](E a,E b){ //按x升序y降序 if(a.x!=b.x) return a.x<b.x; return a.y>b.y; }); for(int i=1; i<=m; ++i) add(e[i].x,e[i].y); //建图 int cs=0,rs=0; bool ok=true; //存在欧拉路 for(int i=1; i<=n; i++){ if(rd[i]!=cd[i]){ if(cd[i]-rd[i]==1) cs++; //出度多1的点数 else if(rd[i]-cd[i]==1) rs++; //入度多1的点数 else ok=false; //度差>1,一定不存在欧拉路 } } if(!(!cs&&!rs || cs==1&&rs==1)) ok=false; //非环非路一定不存在 if(!ok) return puts("No"),0; int start=1; while(!cd[start]) ++start; //找环的起点 for(int i=1; i<=n; i++) if(cd[i]-rd[i]==1){start=i; break;} //找路的起点 dfs(start); //搜索欧拉路 while(!path.empty())printf("%d ",path.top()),path.pop(); return 0; }
浙公网安备 33010602011771号