• 无向图欧拉路径问题


     给定一张无向图,若存在一条从节点 S S S到节点 T T T到路径,恰好不重不漏地经过每条边一次(可以重复经过途中的某个节点)。则称该路径为 S S S T T T的欧拉路。
     特别地,若存在一条从节点 S S S出发的路径,恰好不重不漏地经过每条边一次(可以重复经过途中的某个节点),能够回到节点 S S S,则称该路径为欧拉回路。存在欧拉回路的图叫做欧拉图。

    欧拉图的判定定理

     一张无向图为欧拉图,当且仅当无向图连通并且每一个节点的度数(出入度之和)为偶数。

    欧拉路的判定定理

     一张无向图中存在欧拉路,当且仅当无向图连通,并且图中恰好有两个节点的度数为奇数,其他节点的度数都是偶数。那么这个图中就存在欧拉路,并且这两个度数为奇数的店就是欧拉路的起点 S S S和终点 T T T

    欧拉图的具体求法:

     欧拉图每个节点度数为偶数说明:只要到达一个节点,就必定有一条尚未走过的边可以离开该点故在上面的伪代码中,调用 d f s ( 1 ) dfs(1) dfs(1),不断递归,每次都走从“从 x x x出发的第一条未访问的边”的另一端点 y y y,最终一定能回到节点 1 1 1,产生一条回路。
    在这里插入图片描述

     当然这条回路不能保证经过了图中的每条边,所以dfs函数会继续考虑从x出发的其他未访问的边,找到第二条回路。
    在这里插入图片描述 我们需要把这些回路按适当的方法拼接在一起,形成整张图的欧拉回路。一个很显然的拼接方法是:把第二条回路嵌入第一条回路中间即可。
     但是这个算法的时间复杂度是 O ( N M ) O(NM) O(NM)的,主要原因是一个点会被重复便利多次。即使这个店本身就已经被访问过了。

     假设我们采用邻接表储存无向图,我们可以在访问一条边 ( x , y ) (x,y) (x,y)后,及时修改表头 f i r s t [ x ] first[x] first[x],令它指向吓一跳边。这样我们每次只需要去除 f i r s t [ x ] first[x] first[x],就自然跳过了所有已经访问的边。同时为了避免栈溢出,我们可以手动模拟栈,实现递归与非递归之间的转换,时间复杂度是 O ( N + M ) O(N+M) O(N+M)

    参考代码:

    #include
    #define in read()
    #define MAXN 200050
    #define MAXM MAXN<<1
    #define re register
    using namespace std;
    
    const int N=100050;
    
    int n,m,ans[N],t=0,deg[N];
    int nex[MAXM],first[MAXN],to[MAXM],tot=1;
    bool vis[N];
    
    inline int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} 
    	while(isdigit(c)){x=(x<<3)+(x<<1)+c-'0';c=getchar();}
    	return x*f;
    }
    
    inline void addedge(int u,int v){
    	deg[u]++,deg[v]++;
    	nex[++tot]=first[u];
    	first[u]=tot;
    	to[tot]=v;
    }
    
    stack<int>stk;
    
    void euler(){
    	stk.push(1);
    	while(!stk.empty()){
    		int x=stk.top(),e=first[x];
    		while(e and vis[e])e=nex[e];
    		if(e){
    			int v=to[e];
    			stk.push(v);
    			vis[e]=vis[e^1]=true;
    			first[x]=nex[e];
    		}
    		else{
    			stk.pop();
    			ans[++t]=x;
    		}
    	}
    }
    
    int main(){
    	n=in,m=in;
    	for(re int i=1;i<=m;i++){
    		int u=in,v=in;
    		addedge(u,v);
    		addedge(v,u);
    	}
    	for(re int i=1;i<=n;i++)
    		if(deg[i]%2==1){cout<<"No"<<'\n';return 0;}
    	euler();
    	for(re int i=t;i;i--)
    		cout<<ans[i]<<" "; 
    	return 0;
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    推荐练习:
     1.P6066 [USACO05JAN]Watchcow

  • 相关阅读:
    【Qt】开发环境搭建
    工业互联网:数字化革命的引擎
    Utilizing Transformer Representations Efficiently
    青云霍秉杰:一文读懂Prometheus长期存储主流方案
    使用springboot每日推送早安问候语到用户微信【部署篇】
    Java毕业设计MVC:基于SSM实现计算机硬件评测交流平台
    MATLAB算法实战应用案例精讲-【数模应用】元胞自动机(CA)(附MATLAB和Python实战代码)
    计算机二级python基础题刷题笔记(三)
    React Query 数据库插件:与分布式系统的协作指南
    Java版工程行业管理系统源码-专业的工程管理软件- 工程项目各模块及其功能点清单
  • 原文地址:https://blog.csdn.net/Lucas_FC_/article/details/126346577