哈密顿+欧拉图

Euler回路:图G的一个回路,恰通过G中每条边一次。

A. 连通图

B. 对于无向图,度数为奇数的点的个数为0。

C. 对于有向图,每个顶点的入度等于出度。

Euler通路:一笔画问题

B’.对于无向图,度数为奇数的个数为2。

c’.对于有向图,存在2个顶点,其入度不等于出度,其中起点的出度比入度大1,终点的入度比出度大1。

 

O(m):无向图+链式前向星+DFS

 1 //欧拉回路
 2 const int MAXN = 111111;
 3 int ans[MAXN];//记录边的路径
 4 int ansi = 0;
 5 bool visit[2*MAXN];
 6 void DFS(int now)
 7 {
 8     int k ;
 9     for(k = first[now]; k != -1; k = edge[k].next)
10     {
11         if(!visit[k])
12         {
13             visit[k] = true;
14             visit[k^1] = true;//标记反向边
15             DFS( edge[k].to);
16             ans[ansi++] = k;
17         }
18     }
19 }
Euler 回路

如要记录点的路径,在整层DFS结束之前,保存当前点的序号now。

 



 

Hamilton回路:图G的一个回路,通过G中每个点一次且仅一次。

G为具有n个顶点的无向图。

A. 充分条件:

G中任意两个不同顶点的度数之和大于等于n。

B. 必要条件:

删除任意的一个非空子集S,G-S的连通分支数要不大于原图S的连通分支数。

 

哈密顿回路构造:

前提:充分条件A。

O(n^2):无向图+邻接矩阵(边多)+ reserve函数

  1 //哈密顿回路
  2 inline void reserve(int ans[MAXN],int s,int t)
  3 {
  4     int temp;
  5     while(s < t)
  6     {
  7         temp = ans[s];
  8         ans[s] = ans[t];
  9         ans[t] = temp;
 10         s ++ ;
 11         t -- ;
 12     }
 13 }
 14 void Hamilton(int ans[MAXN],bool map[MAXN][MAXN],int n)
 15 {
 16     int s = 1,t;
 17     int ansi = 2;
 18     int i,j;
 19     int w;
 20     int temp;
 21     bool visit[MAXN] = {false};
 22     for(i = 1;i<n;i++) if(map[s][i])break;//任取邻接s的点为t
 23     t = i;
 24     vist[s] = visit[t] = true;
 25     ans[0] = s;
 26     ans[1] = t;
 27 
 28     while(true)
 29     {
 30         //从1点向外扩展
 31         while(true)
 32         {
 33             for( i = 1;i<n;i++)
 34             {
 35                 if(map[t][i] && !visit[i])
 36                 {
 37                     ans[ansi++] = i;
 38                     visit[i] = true;
 39                     t = i;
 40                     break;
 41                 }
 42             }
 43             if(i > n)break;//无结点可扩展
 44         }
 45 
 46         /*
 47         将当前得到的序列倒置,s与t互换,从t继续扩展,
 48         相当于在原来的序列上从s向外扩展。
 49         */
 50         w = ansi - 1;
 51         i = 0;
 52         reserve(ans, i ,w);
 53         temp = s;
 54         s = t;
 55         t = temp;
 56         //从新的t继续扩展,相当于在原来的序列上从s向外扩展。
 57         while(true)
 58         {
 59             for( i = 1;i<n;i++)
 60             {
 61                 if(map[t][i] && !visit[i])
 62                 {
 63                     ans[ansi++] = i;
 64                     visit[i] = true;
 65                     t = i;
 66                     break;
 67                 }
 68             }
 69             if(i > n)break;//无结点可扩展
 70         }
 71 
 72         //如果s与t不相邻,进行调整
 73         if(!map[s][t])
 74         {
 75             /*
 76             取序列中一个点i,使得ans[i]与t相连,ans[i+1]与s相连
 77             */
 78             for(i = 1;i<ansi - 2 ;i++)
 79                 if(map[ans[i]][t]&&map[s][ans[i+1]])break;
 80             w = ansi - 1;
 81             i ++;
 82             t = ans[i];
 83             reserve(ans, i, w);
 84         }
 85 
 86         //如果当前s与t相连
 87         if(ansi == n)return ;
 88         /*
 89             当前序列中元素个数小于n,
 90             寻找点ans[i],使得ans[i]与ans[]外的一点相连
 91         */
 92         for(j = 1;j<n;j++)
 93         {
 94             if(visit[j]) continue;
 95             for(i = 1;i<ansi - 2;i++)
 96                 if(map[ans[i]][j])break;
 97             if(map[ans[i]][j])break;
 98         }
 99         s = ans[i-1];
100         t = j;
101         //将ans[]中s到ans[i-1]部分的ans[]倒置
102         reserve(ans,0,i-1);
103         //将ans[]中ans[i]到t部分的ans[]倒置
104         reserve(ans,i,ansi - 1);
105         //将点j加入ans[]的尾部
106         ans[ansi++] = j;
107         visit[j] = true;
108     }
109 }
Hamilton 回路

 

思路的分析和证明就略去了,详细参见<图论及应用> 冯林 金博 于瑞云

模板有待测试,之后找些题目试试。然后再贴。

为什么这样是对的? 为什么那样是错的? 凭什么这样是最优的? 凭什么那样不是最优的? 为什么没有更优的解法? 就不能找一个更好的解法吗? 若不知其所以然何苦知其然?
原文地址:https://www.cnblogs.com/PeanutPrince/p/3468480.html