一笔画问题

7683: 一笔画问题

时间限制(普通/Java):1000MS/3000MS     内存限制:65536KByte

描述

如果一个图存在一笔画,则一笔画的路径叫做欧拉路,如果最后又回到起点,那这个路径叫做欧拉回路。

根据一笔画的两个定理,如果寻找欧拉回路,对任意一个点执行深度优先遍历;找欧拉路,则对一个奇点执行dfs,时间复杂度为O(m+n),m为边数,n是点数。

输入

第一行n,m,有n个点,m条边(n<=1000, m<=10000),以下m行描述每条边连接的两点。

输出

欧拉路或欧拉回路,输出一条路径即可。

样例输入

5 5
1 2
2 3
3 4
4 5
5 1

样例输出

1 5 4 3 2 1

题目来源

TZOJ

 

奇点可用于判断一个图形是否能够一笔画出,奇点就是 与该点相连的的线段数为奇数 的点;
 
当一个图形线条之间相通且奇点数为0或者2时,该图形可一笔画出。
 
其中
可以一笔画的图形都由一个或零个欧拉路和非负数个欧拉回路组成(可以只有一个欧拉路没有欧拉回路;也可以没有欧拉路,只有一个欧拉回路;也可有一个欧拉路和多个欧拉回路)
  //我写题时发现的规律,不知道对不对,有没有反例
  
当图形中有欧拉路时,从奇点出发可一笔画完;
  当与两个奇点相连的线段数不为一时(一条,三条,五条...),该奇点肯定存在一个环并且经过该奇点(如上图奇点5,存在环5-6-7-8,经过奇点5),此时图形可以把该环暂时分离出来,并且奇点5的线段数 -2 变为1,以此类推把奇点3旁边的环暂时分离出来,此时连个奇点3 ,5 的线段数都为1,那么剩下的图形坑定为欧拉路,从其中一个奇点开始深搜,不用回溯就可以直接到另一个奇点。
  现在需要复原该图形,需要把之前暂时分离的欧拉回路给组回去,所以从终点的那个奇点开始回溯,回溯的时候将经过的点加入最后该输出的答案的数组里,并且在回溯的时候遍历一遍之前分离的那部分欧拉回路(因为欧拉回路是个环,访问该回路的节点的时候放入答案数组 和 回溯时放入都没有影响)。
  而真正写代码深搜过程中,并没有把图形中奇点所在的所有回路给分离出来 ,而是有部分在一开始寻找唯一一条的欧拉路时就被当做欧拉路的一部分了,对解决问题没有影响。
 
当图形中没有欧拉路,只有欧拉回路时(此时图中只会有一个欧拉回路),与上面深搜遍历分离出来的回路一样,从任意一点开始都可。
 
  当时写深搜,一开始写在刚访问的时候将节点加入答案数组,回溯时需要恢复边未经过的情况,会遍历大部分情况,很浪费时间,提交时直接超时;
  后来在回溯的加入答案数组,不需要恢复边未经过的情况,只需要深搜搜到底,回来的时候将回路加入答案数组,一路到底,只会遍历一次即可的出答案,很快。
  
  这题的深搜与经典深搜不同,根据一笔画(欧拉路)的特殊性采用不同于经典深搜的写法。
  而且该遍历记录的是边是否走过,而不是点是否走过;
 
  AC代码(未优化):
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 vector<int>vec[1010];//记录与该点相连的点,可以不用记录,可以简化代码
 4 bool flag[1011][1010]={0};//记录边
 5 int qi[1010]={0};
 6 int ans[10100];
 7 int sum=0;
 8 int n,m;
 9 int st,en;
10 void dfs(int u)
11 {
12     int len=vec[u].size();
13     for(int i=0;i<len;i++)
14     {
15         if(flag[u][vec[u][i]]==1)
16         {
17             flag[u][vec[u][i]]=0;
18             flag[vec[u][i]][u]=0;
19             dfs(vec[u][i]);
20             ans[sum++]=vec[u][i];//这里回溯的时候加入答案数组;
21         }
22     }
23 }
24 
25 int main()
26 {
27     ios_base::sync_with_stdio(false);//断开与c链接,加快cin与cout
28     cin.tie(nullptr);
29     cout.tie(nullptr);
30     cin>>n>>m;
31     for(int i=0;i<m;i++)//输入边
32     {
33         cin>>st>>en;
34         vec[st].push_back(en);
35         vec[en].push_back(st);
36         qi[st]++;//记录该点相连的线段数,用于下面寻找奇点
37         qi[en]++;
38         flag[st][en]=1;//记录边,1可以走
39         flag[en][st]=1;
40     }
41     int ji=1;
42     for(int i=1;i<=n;i++)//,寻找奇点,一开始设开始的点为1,如果没找到奇点就仍为1,找到奇点就把奇点的值附上去,随便一个奇点出发都可;
43     {
44         if(qi[i]%2==1)ji=i;
45     }
46     dfs(ji);
47     for(int i=0;i<sum;i++)
48     {
49         cout<<ans[i]<<" ";
50     }
51     cout<<ji<<endl;//我这个深搜一开始的奇点回溯时不会经过,所以手动输出了
52     return 0;
53 }

 

 
 
 
 
 
 
 
 
 

 

 

posted @ 2022-07-20 10:00  Minza  阅读(303)  评论(0编辑  收藏  举报