[POJ 1041] John's trip Fleury算法求欧拉回路

题目传送门:【POJ 1041】


题解在题意下面


题目大意:给你一个无向图,一共有 m 个点,n 条边(1 ≤ m ≤ 44,1 ≤ n ≤ 1995),每个点连接着最多不超过 44 条边,每条边都恰好连接两个点。每个点和每条边都有自己的编号,并且任意两个点/两条边的编号不同。现在,Johnny 想要遍历这个无向图,并且要使每一条边都恰好地被走一次。

输入多组数据,每组数据包含三个整数 x , y , z,表示编号为 x , y 的两个点被一条编号为 z 的边连了起来。每组数据输入结束的标志为 x=y=0。当一组数据输入结束后,在新的一组数据中输入 x=y=0 表示输入结束。Johnny 的起点位于每组数据第一行的 x 和 y 中较小的值所对应的点。

对于每组数据,输出为一行整数,代表 Johnny 遍历这个图的次序。优先输出字典序最小的次序。如果他不能遍历完这个图,输出 “Round trip does not exist.” (不含引号)

输入样例:
1 2 1
2 3 2
3 1 6
1 2 5
2 3 3
3 1 4
0 0
1 2 1
2 3 2
1 3 3
2 4 4
0 0
0 0
输出样例:
1 2 3 5 4 6
Round trip does not exist.


题目分析:

这道题,在我分析之前,我一定要认真地看着它,然后,大声地喊道:wqnmlg******!辣鸡出题人连个题意都不好好写!

本题极为毒瘤,题意“按字典序输出”,却设一个 Special Judge;判断有欧拉回路的条件竟是……只需要判断出入度即可???Excuse me?


回到题意。

由题,除去一系列干扰项,这道题就是求一个欧拉回路,并且只需要随便输出一种遍历次序即可。

所以就有两种方法:dfs,或者是像本题一样用 Fleury 算法求欧拉回路。要判断 Johnny 能否回到原来的位置,只需要看每个点的度数是否为奇数即可(任意一个点是奇数则不能返回,所有的点都不是奇数则可以返回)。之后直接使用 Fleury 算法求出欧拉回路即可。

要注意的是,需要额外开一个栈来保存每次走的边的编号,最后直接输出编号即可。

如果想要了解 Fleury 算法的核心,可以参考这篇文章:Fleury(弗罗莱)算法求欧拉路径

下面附上代码:

  1. #include<cstdio>  
  2. #include<cstring>  
  3. #include<algorithm>  
  4. #include<stack>  
  5. using namespace std;  
  6. const int MX=80005;  
  7.   
  8. struct Edge{  
  9.     int to,next,num;  
  10. }edge[MX*2];  
  11. int head[MX],now=1,ss,deg[MX];          //初始化为 1,实际从 2 开始,方便之后异或   
  12. bool vis[MX*2];  
  13. stack<int> s,eg;  
  14.   
  15. inline void adde(int u,int v,int no){  
  16.     edge[++now].to=v;  
  17.     edge[now].num=no;  
  18.     edge[now].next=head[u];  
  19.     head[u]=now;  
  20. }  
  21. void dfs(int u){  
  22.     s.push(u);  
  23.     for (int i=head[u];i>1;i=edge[i].next){  
  24.         if (!vis[i]){                   //判断每一条边是否经过   
  25.             vis[i]=vis[i^1]=true;  
  26.             eg.push(edge[i].num);  
  27.             dfs(edge[i].to);  
  28.             break;  
  29.         }  
  30.     }  
  31. }  
  32. void fleury(int u){  
  33.     s.push(u);  
  34.     while (!s.empty()){  
  35.         bool can_go=false;  
  36.         int u=s.top();  
  37.         s.pop();                        //此时出栈,方便在dfs中将点加入栈   
  38.         for (int i=head[u];i>1;i=edge[i].next){  
  39.             if (!vis[i]){                       //有路可走   
  40.                 can_go=true;  
  41.                 break;  
  42.             }  
  43.         }  
  44.         if (!can_go){  
  45.             if (!eg.empty()) printf(“%d ”,eg.top());  
  46.             eg.pop();  
  47.         }  
  48.         else dfs(u);  
  49.     }  
  50. }  
  51. void _init(){  
  52.     memset(deg,0,sizeof(deg));  
  53.     memset(edge,0,sizeof(edge));  
  54.     memset(head,0,sizeof(head));  
  55.     memset(vis,0,sizeof(vis));  
  56.     while (!s.empty()) s.pop();  
  57.     while (!eg.empty()) eg.pop();  
  58.     now=1;  
  59. }  
  60.   
  61. int main(){  
  62.     int a,b,num;                //a,b:两个点的编号   num:边的编号   
  63.     num=0;  
  64.       
  65.     while (1){  
  66.         bool ok=true;  
  67.         scanf(”%d%d”,&a,&b);  
  68.         if (a==0 && b==0) break;  
  69.           
  70.         _init();  
  71.           
  72.         ss=min(a,b);            //第一行较小的那个就是Johnny住的地方   
  73.         scanf(”%d”,&num);  
  74.         adde(a,b,num),adde(b,a,num);  
  75.         ++deg[a],++deg[b];  
  76.           
  77.         while (1){  
  78.             scanf(”%d%d”,&a,&b);  
  79.             if (a==0 && b==0) break;  
  80.             scanf(”%d”,&num);  
  81.             adde(a,b,num),adde(b,a,num);  
  82.             ++deg[a],++deg[b];  
  83.         }  
  84.           
  85.         for (int i=1;i<=44;i++){ //判断每个点的度???? (总共只有44个点)   
  86.             if (deg[i]&1){  
  87.                 printf(”Round trip does not exist.\n”);  
  88.                 ok=false;  
  89.                 break;  
  90.             }  
  91.         }  
  92.         if (!ok) continue;  
  93.           
  94.         fleury(ss);  
  95.         printf(”\n”);  
  96.     }  
  97.     return 0;  
  98. }  
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值