D165【模板】有向图 欧拉路径 欧拉回路 P7771 欧拉路径

D165【模板】有向图 欧拉路径 欧拉回路 P7771 欧拉路径_哔哩哔哩_bilibili

 

每条边恰好经过一次的路径称为欧拉路径,每条边恰好经过一次又回到起点的路径称为欧拉回路,通称一笔画的路径

有向图的欧拉回路判定:所有点的入度等于出度,如图 1

有向图的欧拉路径判定:有且仅有两个点,入度不等于出度。出度比入度多1的点做起点;入度比出度多1点做终点,如图 2

图 3 存在 4 个点不满足条件,不存在欧拉路径

 

P7771 【模板】欧拉路径 - 洛谷

求有向图字典序最小的欧拉路径。$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;
}
 

 

posted @ 2026-05-26 06:51  董晓  阅读(44)  评论(0)    收藏  举报