哈密顿图(Hamilton)

概念:

  哈密顿图:图G的一个回路,若它通过图的每一个节点一次,且仅一次,就是哈密顿回路.存在哈密顿回路的图就是哈密顿图.哈密顿图就是从一点出发,经过所有的必须且只能一次,最终回到起点的路径.图中有的边可以不经过,但是不会有边被经过两次.

  与欧拉图的区别:欧拉图讨论的实际上是图上关于边的可行便利问题,而哈密顿图的要求与点有关.

判定:

一:Dirac定理(充分条件)

  设一个无向图中有N个顶点,若所有顶点的度数大于等于N/2,则哈密顿回路一定存在.(N/2指的是⌈N/2⌉,向上取整)

二:基本的必要条件

  设图G=<V, E>是哈密顿图,则对于v的任意一个非空子集S,若以|S|表示S中元素的数目,G-S表示G中删除了S中的点以及这些点所关联的边后得到的子图,则W(G-S)<=|S|成立.其中W(G-S)是G-S中联通分支数.

三:竞赛图(哈密顿通路)

  N(N>=2)阶竞赛图一点存在哈密顿通路.

算法:

一:在Dirac定理的前提下构造哈密顿回路

过程:

  1:任意找两个相邻的节点S和T,在其基础上扩展出一条尽量长的没有重复结点的路径.即如果S与结点v相邻,而且v不在路径S -> T上,则可以把该路径变成v -> S -> T,然后v成为新的S.从S和T分别向两头扩展,直到无法继续扩展为止,即所有与S或T相邻的节点都在路径S -> T上.

  2:若S与T相邻,则路径S -> T形成了一个回路.

  3:若S与T不相邻,可以构造出来一个回路.设路径S -> T上有k+2个节点,依次为S, v1, v2, ..., vk, T.可以证明存在节点vi(i属于[1, k]),满足vi与T相邻,且vi+1与S相邻.找到这个节点vi,把原路径变成S -> vi -> T -> vi+1 -> S,即形成了一个回路.

  4:到此为止,已经构造出来了一个没有重复节点的的回路,如果其长度为N,则哈密顿回路就找到了.如果回路的长度小于N,由于整个图是连通的,所以在该回路上,一定存在一点与回路之外的点相邻.那么从该点处把回路断开,就变回了一条路径,同时还可以将与之相邻的点加入路径.再按照步骤1的方法尽量扩展路径,则一定有新的节点被加进来.接着回到路径2.

证明:

  可利用鸽巢原理证明.

伪代码:

  设s为哈密顿回路的起始点,t为哈密顿回路中终点s之前的点.ans[]为最终的哈密顿回路.倒置的意思指的是将数组对应的区间中数字的排列顺序方向.

  1:初始化,令s = 1,t为s的任意一个邻接点.

  2:如果ans[]中元素的个数小于n,则从t开始向外扩展,如果有可扩展点v,放入ans[]的尾部,并且t=v,并继续扩展,如无法扩展进入步骤3.

  3:将当前得到的ans[]倒置,s和t互换,从t开始向外扩展,如果有可扩展点v,放入ans[]尾部,并且t=v,并继续扩展.如无法扩展进入步骤4.

  4:如果当前s和t相邻,进入步骤5.否则,遍历ans[],寻找点ans[i],使得ans[i]与t相连并且ans[i +1]与s相连,将从ans[i + 1]到t部分的ans[]倒置,t=ans[i +1],进如步骤5.

  5:如果当前ans[]中元素的个数等于n,算法结束,ans[]中保存了哈密顿回路(可看情况是否加入点s).否则,如果s与t连通,但是ans[]中的元素的个数小于n,则遍历ans[],寻找点ans[i],使得ans[i]与ans[]外的一点(j)相连,则令s=ans[i - 1],t = j,将ans[]中s到ans[i - 1]部分的ans[]倒置,将ans[]中的ans[i]到t的部分倒置,将点j加入到ans[]的尾部,转步骤2.

时间复杂度:

  如果说每次到步骤5算一轮的话,那么由于每一轮当中至少有一个节点被加入到路径S -> T中,所以总的轮数肯定不超过n轮,所以时间复杂度为O(n^2).

代码:

 1 const int maxN = 100;
 2 inline void reverse(int arv[maxN + 7], int s, int t){//将数组anv从下标s到t的部分的顺序反向
 3     int temp;
 4     while(s  < t){
 5         temp = arv[s];
 6         arv[s] = arv[t];
 7         arv[t] = temp;
 8         s++;
 9         t--;
10     }
11 }
12
13 void Hamilton(int ans[maxN + 7], bool map[maxN + 7][maxN + 7], int n){
14     int s = 1, t;//初始化取s为1号点
15     int ansi = 2;
16     int i, j;
17     int w;
18     int temp;
19     bool visit[maxN + 7] = {false};
20     for(i = 1; i <= n; i++) if(map[s][i]) break;
21     t = i;//取任意邻接与s的点为t
22     visit[s] = visit[t] = true;
23     ans[0] = s;
24     ans[1] = t;
25     while(true){
26         while(true){//从t向外扩展
27             for(i = 1; i <= n; i++){
28                 if(map[t][i] && !visit[i]){
29                     ans[ansi++] = i;
30                     visit[i] = true;
31                     t = i;
32                     break;
33                 }
34             }
35             if(i > n) break;
36         }
37         w = ansi - 1;//将当前得到的序列倒置,s和t互换,从t继续扩展,相当于在原来的序列上从s向外扩展
38         i = 0;
39         reverse(ans, i, w);
40         temp = s;
41         s = t;
42         t = temp;
43         while(true){//从新的t继续向外扩展,相当于在原来的序列上从s向外扩展
44             for(i = 1; i <= n; i++){
45                 if(map[t][i] && !visit[i]){
46                     ans[ansi++] = i;
47                     visit[i] = true;
48                     t = i;
49                     break;
50                 }
51             }
52             if(i > n) break;
53         }
54         if(!map[s][t]){//如果s和t不相邻,进行调整
55             for(i = 1; i < ansi - 2; i++)//取序列中的一点i,使得ans[i]与t相连,并且ans[i+1]与s相连
56                 if(map[ans[i]][t] && map[s][ans[i + 1]])break;
57             w = ansi - 1;
58             i++;
59             t = ans[i];
60             reverse(ans, i, w);//将从ans[i +1]到t部分的ans[]倒置
61         }//此时s和t相连
62         if(ansi == n) return;//如果当前序列包含n个元素,算法结束
63         for(j = 1; j <= n; j++){//当前序列中元素的个数小于n,寻找点ans[i],使得ans[i]与ans[]外的一个点相连
64             if(visit[j]) continue;
65             for(i = 1; i < ansi - 2; i++)if(map[ans[i]][j])break;
66                 if(map[ans[i]][j]) break;
67         }
68         s = ans[i - 1];
69         t = j;//将新找到的点j赋给t
70         reverse(ans, 0, i - 1);//将ans[]中s到ans[i-1]的部分倒置
71         reverse(ans, i, ansi - 1);//将ans[]中ans[i]到t的部分倒置
72         ans[ansi++] = j;//将点j加入到ans[]尾部
73         visit[j] = true;
74     }
75 }
时间: 2024-10-05 08:45:05

哈密顿图(Hamilton)的相关文章

哈密顿图的判定

推荐学习资料: http://www.cnblogs.com/Ash-ly/p/5452580.html http://ylroki.blog.163.com/blog/static/162978871201032775322518/ https://wenku.baidu.com/view/38dd0d4714791711cd791725.html 一.定义 通过图G的每个节点一次,且仅一次的通路称为哈密顿通路 通过图G的每个节点一次,且仅一次的回路称为哈密顿回路 含有哈密顿回路的图称为哈密顿

AGC018D - Tree and Hamilton Path

题意 给出一个n个点的带边权的树,再给出一个n个点的完全图,其中每两个点之间的距离为这两个点在树上的距离,求最大的哈密顿图. 做法 直接考虑在树上的游历,如果存在一条边把树分成大小相同的两半,然后在两半中的点中交替走,这样子显然是最优的,因为每条边都会达到可能的最多的访问次数:否则必然存在一个点(重心),去除这个点之后的森林里每棵树的大小都不大于n/2,这样最优的游历一定是从每棵树出来走到另一棵中没走过的点,这样的安排总是存在的.考虑到最后并不会再访问重心,取重心连出去的权值最小的一条边来承担这

Hamilton回路的判定与构造

定理1:在一个具有n个顶点的无向连通图G中,如果任意两个顶点的度数之和大于n,则G具有Hamilton回路.此条件为充分条件 定理2:设图G = <V,E>,是Hamilton图,则对于v的任意一个非空子集S,若以|S|表示S中元素数目,G-S表示G中删除了S中的点以及与这些点关联的边后得到的子图,则满足G-S的连通分支数W(G-S)<=|S|.此条件为必要条件. 构造Hamilton回路的算法过程,分成以下几个步骤: 1. 任意找两个相邻的节点 S 和 T,在它们基础上扩展出一条尽量长

状压DP HDU3538 A sample Hamilton path

A sample Hamilton path Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 527    Accepted Submission(s): 213 Problem Description Give you a Graph,you have to start at the city with ID zero. Input T

离散数学-图论-哈密顿图及其应用

哈密顿图 一.定义概念 1.哈密顿通路 设G=<V,E>为一图(无向图或有向图).G中经过每个顶点一次且仅一次的通路称作哈密顿通路 2.哈密顿回路 G中经过每个顶点一次且仅一次的回路(通路基础上+回到起始点)称作哈密顿回路 3.哈密顿图 若G中存在哈密顿回路,则称它是哈密顿图 4.定义详解: (1)存在哈密顿通路(回路)的图一定是连通图: (2)哈密顿通路是初级通路,哈密顿回路是初级回路: (3)若G中存在哈密顿回路,则它一定存在哈密顿通路,反之不真(看课本的话,是必要条件,而不是充分条件,故

PJOI 1024 Hamilton Circles 矩阵快速幂

题意:给定2*2*n的立方体 我们认为1*1*1 的小格子是一个顶点 有公共面的顶点认为有一条无向边 给定n 问有多少条哈密顿回路 结论:a[n] = 4*a[n-1] - a[n-2]; (n>=3) 别问我怎么知道的,我也不知道...TAT 然后有注意一点,这里面涉及到了减法,矩阵快速幂后要注意ans可能<0 ,加个mod即可. #include"cstdio" #include"iostream" #include"queue"

HDU3538 A sample Hamilton path

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 503    Accepted Submission(s): 200 Problem Description Give you a Graph,you have to start at the city with ID zero. Input The first line is n(1<=n

哈密顿图 BestCoder Round #53 (div.2) 1003 Rikka with Graph II

题目传送门 题意:判断是否为哈密顿图 分析:首先一种情况是不合法的:也就是度数为1的点超过2个:合法的有:,那么从度数为1的点开始深搜,如果存在一种走法能够走完n个点那么存在哈密顿路 收获:学习资料 代码: /************************************************ * Author :Running_Time * Created Time :2015-8-29 20:37:34 * File Name :C.cpp *******************

判断是否是哈密顿图--HDU 5424

题意:给一张无向图,判断是否是哈密顿图. 哈密顿路:经过每个点有且仅有一次的一条通路. 方法:每次找度数最小的点作为起点,然后dfs整个图,看能遍历到的点的数目能否达到n. 1 #include<iostream> 2 #include<cstdio> 3 #include<ctime> 4 #include<cmath> 5 #include<cstring> 6 #include<algorithm> 7 #include<