欧拉图详解

欧拉图详解

        通过图(无向图或有向图)中所有边一次且仅一次行遍图中所有顶点的通路称为欧拉通路,通过图中所有边一次且仅一次行遍所有顶点的回路称为欧拉回路。具有欧拉回路的图称为欧拉图(Euler Graph),具有欧拉通路而无欧拉回路的图称为半欧拉图

        1 定义

        欧拉通路(Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。

        欧拉回路 (Eulercircuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。

        2 无向图是否具有欧拉通路或回路的判定

        G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。

        G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。

        3 有向图是否具有欧拉通路或回路的判定

        D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。

        D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。

        (注意:这里说有向图连通,说的是有向图是弱连通图。即把有向图中的边变成无向边,只要该图连通,那么原有向图即是弱连通图。实际中可用并查集判断是否弱连通)

         注意下面打印节点的代码,其实打印欧拉路径的节点轨迹可以先打印每条欧拉路上的边,然后取每条边的头结点即可(最后一条边还需要取尾部结点)

         对于一般的单词首尾相连的问题,一般都是转化为有向图的欧拉通路问题(而不是无向图),比如”给你多个单词,问你能不能将所有单词组成这样一个序列:序列的前一个单词的尾字母与后一个单词的头字母相同”,如果你把每个单词看出无向的边,那么最终求出的欧拉通路可能存在两个单词尾部和尾部相连的情况。

         无向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:

[cpp]  view plain  copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5. const int maxn=100+5;  
  6.   
  7. //无向图打印欧拉路径或回路  
  8. //输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的无向图  
  9.   
  10. int n;//图中顶点,顶点编号1到n  
  11. int m;//图中边  
  12. int G[maxn][maxn];  
  13. int vis[maxn][maxn];//vis[i][j]==1表示i与j点直接存在一条边  
  14.   
  15. //打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)  
  16. //打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的  
  17. //如果是打印欧拉路径,那么输入的u一定要是起点之一,即度数为奇数的点之一  
  18. //否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已  
  19. void euler(int u)  
  20. {  
  21.     for(int v=1;v<=n;v++)if(vis[u][v]||vis[v][u])  
  22.     {  
  23.         //递归思想,去掉了u与v这条边,  
  24.         //余图还是一个具有欧拉道路的图,且v变成一个起点了  
  25.         vis[u][v]=vis[v][u]=0;  
  26.         euler(v);  
  27.         printf("%d %d\n",u,v);  
  28.     }  
  29. }  
  30.   
  31. //输出欧拉回路或路径上按顺序经过的节点  
  32. //u也必须要是起点之一,否则输出的也是乱序点而已  
  33. void euler_point(int u)  
  34. {  
  35.     for(int v=1;v<=n;v++)if(vis[u][v] || vis[v][u])  
  36.     {  
  37.         vis[u][v]=vis[v][u]=0;  
  38.         euler_point(v);  
  39.     }  
  40.     printf("%d\n",u);  
  41. }  
  42.   
  43. int main()  
  44. {  
  45.     while(scanf("%d%d",&n,&m)==2)  
  46.     {  
  47.         memset(G,0,sizeof(G));  
  48.         memset(vis,0,sizeof(vis));  
  49.         for(int i=1;i<=m;i++)  
  50.         {  
  51.             int u,v;  
  52.             scanf("%d%d",&u,&v);  
  53.             G[u][v]=G[v][u]=1;//无向图  
  54.             vis[u][v]=vis[v][u]=1;  
  55.         }  
  56.   
  57.         int u;  
  58.         scanf("%d",&u);  
  59.         euler_point(u);  
  60.   
  61.     }  
  62.     return 0;  
  63. }  

         有向欧拉图(半欧拉图)逆序打印欧拉回路或通路代码:

[cpp]  view plain  copy
  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. using namespace std;  
  5. const int maxn=100+5;  
  6.   
  7. //有向图打印欧拉路径或回路  
  8. //输入保证是一个n顶点,m条边的具有欧拉回路或欧拉路径的有向图  
  9.   
  10. int n;//图中顶点,顶点编号1到n  
  11. int m;//图中边  
  12. int G[maxn][maxn];  
  13. int vis[maxn][maxn];//vis[i][j]==1表示i到j点存在一条边  
  14.   
  15. //打印欧拉路径或欧拉回路(必须本图有欧拉路径或回路才行)  
  16. //打印的结果中最后一条边才是u开始的,打印的第一条边不一定是u开始的  
  17. //如果是打印欧拉路径,那么输入的u一定要是起点之一,即abs(入度-出度)==1  
  18. //且如果是出度-入度==1的点,那么该点就应该是欧拉图的起点,但是会逆序输出,所以最后才输出  
  19. //否则euler打印的的边不会构成欧拉路径,只不过是乱序打印图中所有的边而已  
  20. void euler(int u)  
  21. {  
  22.     for(int v=1;v<=n;v++)if(vis[u][v])  
  23.     {  
  24.         //递归思想,去掉了u与v这条边,  
  25.         //余图还是一个具有欧拉道路的图,且v变成一个起点了  
  26.         vis[u][v]=0;  
  27.         euler(v);  
  28.         printf("%d %d\n",u,v);  
  29.     }  
  30. }  
  31.   
  32. //逆序输出欧拉回路或路径上按顺序经过的节点  
  33. //u也必须要是起点之一,否则输出的也是乱序点而已  
  34. void euler_point(int u)  
  35. {  
  36.     for(int v=1;v<=n;v++)if(vis[u][v])  
  37.     {  
  38.         vis[u][v]=0;  
  39.         euler_point(v);  
  40.     }  
  41.     printf("%d\n",u);  
  42. }  
  43.   
  44. int main()  
  45. {  
  46.     while(scanf("%d%d",&n,&m)==2)  
  47.     {  
  48.         memset(G,0,sizeof(G));  
  49.         memset(vis,0,sizeof(vis));  
  50.         for(int i=1;i<=m;i++)  
  51.         {  
  52.             int u,v;  
  53.             scanf("%d%d",&u,&v);  
  54.             G[u][v]=1;//有向图  
  55.             vis[u][v]=1;  
  56.         }  
  57.   
  58.         int u;  
  59.         scanf("%d",&u);  
  60.         euler(u);  
  61.   
  62.     }  
  63.     return 0;  
  64. }  

 

欧拉图应用

POJ 2513 Colored Sticks(欧拉回路判断+字典树Trie+并查集):其实就是判定是否存在欧拉图,首先欧拉图必须连通,所以需要用并查集。然后本图中的每个节点是一个单词,所以需要用单词映射成节点编号(用map<string,int>映射可能会超时),所以需要用字典序映射。解题报告

POJ 1041 John's trip(欧拉回路+输出路径):输出字典序最小的欧拉回路。解题报告!

POJ 2337 Catenyms(有向欧拉图:输出欧拉路径):有向图输出字典序最小的欧拉路径。解题报告

POJ 1300 Door Man(欧拉回路判定):判断无向图是否存在欧拉通路。解题报告!

POJ 2230 Watchcow(欧拉回路:输出点轨迹):输出有向图的欧拉回路点轨迹。解题报告

POJ 1386 Play on Words(判定图欧拉通路是否存在):有向图欧拉道路问题。解题报告!

HDU 1878 欧拉回路(简单欧拉回路判定):简单无向图欧拉问题判定。解题报告

HDU 3018 Ant Trip(欧拉回路:一笔画问题):给你一个无向的欧拉图,你需要几笔才能一笔画完整个图?解题报告

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值