Chinaunix首页 | 论坛 | 博客
  • 博客访问: 13076
  • 博文数量: 3
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-21 04:16
文章分类

全部博文(3)

文章存档

2017年(1)

2015年(1)

2013年(1)

我的朋友

分类: C/C++

2015-11-09 23:43:37

欧拉回路

画一笔画时,整幅图一笔画成,线条之间可以交叉(两次通过同一个点),但不会重复,也不会断掉,这就是欧拉路径
如果最后又回到起点,则称作欧拉回路。一个欧拉图可以有多个欧拉回路。

欧拉路,既可以在有向图上,也可以在无向图上,只是有向图对每条边经过的方向进行了限定。

条件

一幅图存在欧拉路的条件,也即这幅图可以用一笔画成的条件是

  • 有向图,如果是弱连通的,且所有的顶点,入度等于出度,那么该图存在欧拉回路,这是充要条件
  • 有向图,如果是弱连通的,且除开两个顶点,其他点得入度都等于出度,并且这两个顶点,一个入度比出度多1,另一个出度比入度少1,那么这个图虽然不存在欧拉回路,但是有欧拉路径,且是充要条件。
  • 无向图,连通,且所有的顶点的度为偶数,存在欧拉回路的充要条件
  • 无向图,连通,且除了两个顶点,其余顶点的度都是偶数,则虽然没有欧拉回路,但存在欧拉路径
  • 无向图,连通,且可分为若干个,边不相交的环,则是欧拉图。

算法

深度优先,判断当前顶点出发的边是否已经访问,这样,dfs回溯的时候,当前顶点所有的边都已经遍历过了。

vis[边]而不是通常的vis[顶点],按定义回路只访问边一次,点没有限制,还因为有可能有重边,自环的情况

模板:

点击(此处)折叠或打开

  1. Euler 模板:深搜,有重复的边、自环,所以要区分边,用边的编号,确保每边只访问一次,点不受限制
  2.             

  3.     dfs(int x){
  4.         for each next: y{
  5.             if(vis[])
  6.                 continue;
  7.             vis[] = 1;
  8.             dfs(y);
  9.             path.push_back((x-y));
  10.         }
  11.     }
  12.     实际输出的是 path的倒序

poj 1041



点击(此处)折叠或打开

  1. const int maxn = 1995;
  2. const int maxm = 1000000;
  3. vector< pair<int, int> > adj[maxn];
  4. int vis[maxm];

  5. int fa[maxn];

  6. /*

  7. 前向星结构

  8. */

  9. int head[man];
  10. int to[maxm*2];
  11. int link[maxm*2];
  12. int index = 0;

  13. void add_edge(int x, int y, int z){
  14.     index++;

  15.     to[index] = y;
  16.     link[index] = head[x];
  17.     head[x] = index;
  18. }

  19. #define CLR(x) memset((x), 0, sizeof((x)))


  20. #define eid first
  21. #define vtx second

  22. void add(int x, int y, int z){
  23.     
  24.     adj[x].push_back(make_pair(z, y));
  25.     adj[y].push_back(make_pair(z, x));
  26. }

  27. int get_fa(int x){
  28.     
  29.     return x == fa[x]?x:fa[x] = get_fa(fa[x]);
  30. }
  31. void dfs(int x, vector<int> &path){
  32.     
  33.     for(int i = 0; i < adj[x].size(); ++i){
  34.         int e = adj[x][i].eid;
  35.         int y = adj[x][i].vtx;
  36.         if(vis[e])
  37.             continue;
  38.         vis[e] = 1;
  39.         dfs(y, path);
  40.         path.push_back(e); /*  在dfs后 */
  41.     }
  42. }
  43. void init(){
  44.         for(int i = 0; i < maxn; i++)
  45.                 adj[i].clear();
  46.         memset(fa, 0, sizeof(fa));
  47.         CLR(head);CLR(to);CLR(link);
  48.         index = 0;
  49. }
  50. bool solve(vector<int> &path){

  51.     /* 判断 连通性 */
  52.     for(int i = 0; i < maxn; i++)
  53.         fa[i] = i;
  54.     for(int i = 0; i < maxn; i++){
  55.         for(int j = 0; j < adj[i].size(); j++)
  56.             fa[get_fa(i)] = get_fa(adj[i][j].vtx);

  57.     }

  58.     int origin = -1;
  59.     for(int i = 1; i < maxn; i++){
  60.         if(adj[i].empty())
  61.             continue;
  62.         if(adj[i].size()%2 == 1) return false;
  63.         if(origin == -1)
  64.                 origin = i;
  65.         if(get_fa(i) != get_fa(origin)) return false;

  66.         sort(adj[i].begin(), adj[i].end());
  67.     }

  68.     if(origin == -1) return false;

  69.     path.clear();
  70.     memset(vis, 0, sizeof(vis));

  71.     dfs(origin, path);
  72.     reverse(path.begin(), path.end()); /*  逆序 */

  73.     return true;
  74. }

  75. int main(){
  76.     
  77.     int x,y,z;
  78.     while(scanf("%d%d", &x, &y)){
  79.         if(x == 0 && y == 0)
  80.             break;
  81.         scanf("%d", &z);
  82.         init();
  83.         add(x, y, z);
  84.         while(scanf("%d%d", &x, &y)){
  85.             if(x == 0 && y == 0)
  86.                 break;
  87.             scanf("%d", &z);
  88.             add(x, y, z);
  89.         }

  90.         vector<int> path;
  91.         
  92.         if(!solve(path)){
  93.             printf("Round trip does not exist.\n");
  94.         }else{
  95.             int i;
  96.             for(i = 0; i < path.size()-1; i++)
  97.                 printf("%d ", path[i]);
  98.             printf("%d\n", path[i]);
  99.         }
  100.     }


  101.     return 0;
  102. }

总结


  • 以边为访问标记而不是顶点
  • dfs只进行一次就可以,顶点对于欧拉回路可以随意选择
  • path的添加在dfs回溯之后进行,每次添加的是无路可进的边,添加的顺序,是dfs最先访问完的边

阅读(886) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~