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 }
如要记录点的路径,在整层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 }
思路的分析和证明就略去了,详细参见<图论及应用> 冯林 金博 于瑞云
模板有待测试,之后找些题目试试。然后再贴。
为什么这样是对的?
为什么那样是错的?
凭什么这样是最优的?
凭什么那样不是最优的?
为什么没有更优的解法?
就不能找一个更好的解法吗?
若不知其所以然何苦知其然?