• 「学习笔记」欧拉回路


    「学习笔记」欧拉回路

    定义

    (G(V,E)) 是一张图
    欧拉回路 : 图 (G) 中经过每条边一次并且仅一次的回路.
    欧拉路径 : 图 (G) 中经过每条边一次并且仅一次的路径.
    欧拉图 : 存在欧拉回路的图.
    半欧拉图 : 存在欧拉路径但不存在欧拉回路的图.

    定理与性质

    以下的图中, 默认把孤立点删除, 因为这样不会影响原图是否为欧拉图.

    定理1 : 无向图 (G) 为欧拉图, 当且仅当 (G) 为连通图且所有顶点的的度为偶数.
    推论1 : 无向图 (G) 为半欧拉图, 当且仅当 (G) 为连通图, 且除了两个顶点的度为奇数外, 其他顶点的度为偶数.

    定理2 : 有向图 (G) 为欧拉图, 当且仅当 (G) 为连通图且所有顶点的入度等于出度.
    推论2 : 有线图 (G) 为半欧拉图, 当且仅当 (G) 为连通图, 且存在顶点 (u) 的出度比入度大 (1), 顶点 (v) 的入读比出度大 (1), 其他顶点的入度等于出度.

    性质1 : 设 (C) 是欧拉图 (G) 的一个简单回路, 将 (C) 中的边从图 (G) 中删去得到一个新图 (G'), 则 (G') 的每一个极大连通子图都存在一条欧拉回路.

    性质2 : 设 (C_1,C_2) 是图 (G) 的两个没有公共边, 但有至少一个公共点的简单回路, 则可以将它们合并成一个新的简单回路 (C').

    求欧拉回路

    过程

    (dfs), 当一个点的所有边都被遍历过后, 将这个点加入答案的栈中, 最后从栈顶开始输出答案.

    其实这个算法叫做 Hierholzer 算法, 它的想法是每次找出一条回路, 然后从图上删去这个回路, 再从回路上的每个节点开始去找新的回路, 找到新的回路后再把它和原来的回路合并. 根据上面的 性质1 和 性质2, 可以证明若图上存在一条欧拉回路, 这样一定能够找出来.

    而上述的过程其实就相当于对整张图 (dfs) 一遍, 每次 (dfs) 到顶时就相当于找到了一条回路. 假设这条回路上存在一条边 ((u,v)), 那么沿着 ((u,v')) 继续 (dfs) 就相当于找一条新的回路. 一个点的所有边都被遍历过后再把这个点加入栈中, 就相当于合并若干个环.

    代码

    欧拉回路
    这个板子相对麻烦一点, 因为它要同时处理有向图和无向图的情况, 并且要求输出边的方案.

    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int _=1e5+7;
    const int __=4e5+7;
    
    int n,m,ty,st,de[_][2];
    int lst[_],nxt[__],to[__],id[__],tot=1;
    int ans[__],top;
    bool ban[__];
    
    int gi(){
    	int x=0; char c=getchar();
    	while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9'){ x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
    	return x;
    }
    
    void Add(int x,int y,int t,int ty){
    	nxt[++tot]=lst[x]; to[tot]=y; id[tot]=t; lst[x]=tot;
    	if(ty==1){ nxt[++tot]=lst[y]; to[tot]=x; id[tot]=-t; lst[y]=tot; }
    }
    
    void Init(){
    	ty=gi(),n=gi(),m=gi();
    	int x,y;
    	for(int i=1;i<=m;i++){
    		x=gi(),y=gi();
    		Add(x,y,i,ty);
    		if(ty==1) de[x][0]++,de[y][0]++;
    		else de[x][1]++,de[y][0]++;
    	}
    }
    
    void Dfs(int u,int t){
    	while(lst[u]){
    		int i=lst[u];
    		if(ban[i]){ lst[u]=nxt[i]; continue; }           // 需要及时更新 lst, 否则会被卡到 m^2
    		ban[i]=1;
    		if(ty==1) ban[i^1]=1;
    		lst[u]=nxt[i];
    		Dfs(to[i],t+1);
    		ans[++top]=id[i];
    	}
    }
    
    void Run(){
    	for(int i=1;i<=n;i++)
    		if(ty==1&&de[i][0]%2){ puts("NO"); return; }
    		else if(ty==2&&de[i][0]!=de[i][1]){ puts("NO"); return; }
    	for(int i=1;i<=n;i++)
    		if(de[i][0]){ st=i; break; }
    	Dfs(st,1);
    	for(int i=2;i<=tot;i++)
    		if(!ban[i]){ puts("NO"); return; }
    	puts("YES");
    	for(int i=m;i>=1;i--)
    		printf("%d ",ans[i]);
    	putchar('
    ');
    }
    
    int main(){
    #ifndef ONLINE_JUDGE
    	freopen("x.in","r",stdin);
    	//freopen("x.out","w",stdout);
    #endif
    	Init();
    	Run();
    	return 0;
    }
    
    
  • 相关阅读:
    js导出excel增加表头、mso-number-format定义数据格式
    $.each() 与 $(selector).each()的区别
    【Linux常用命令】Linux kill, killall, kill -9,
    Shell 语法报错记录
    【常用脚本记录1----挂测问题脚本】
    【】系统一启动就会运行的程序
    【WAN】PPPOE宽带上网功能详解
    解决linux awk:line的问题
    Ubuntu 16.04安装测试MQTT Mosquitto
    【TCP协议详解】
  • 原文地址:https://www.cnblogs.com/BruceW/p/11847740.html
Copyright © 2020-2023  润新知