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,在它们基础上扩展出一条尽量长的没有重复节点的路径。也就是说,如果 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 相邻。证明方法也是根据鸽巢原理,既然与 S 和 T 相邻的点都在该路径上,它们分布的范围只有 v1 ~ vk 这 k 个点, k ≤ N -
2,而 d(S) + d(T) ≥ N,那么可以想像,肯定存在一个与 S 相邻的点 vi 和一个与 T 相邻的点 vj, 满足 j <
i。那么上面的命题也就显然成立了。

找到了满足条件的节点 vi 以后,就可以把原路径变成 S → vi+1 → T → vi → S,即形成了一个回路。
4.
现在我们有了一个没有重复节点的回路。如果它的长度为 N,则汉密尔顿回路就找到了。

如果回路的长度小于 N,由于整个图是连通的,所以在该回路上,一定存在一点与回路以外的点相邻。那么从该点处把回路断开,就变回了一条路径。再按照步骤 1
的方法尽量扩展路径,则一定有新的节点被加进来。接着回到步骤 2。

模板题:POJ 2438 or HDU 4337 Childrens Dining

问题是求小朋友围着桌子的座次就是求原图中的一个环,但是要求这个环不能包含所给出的每条边,所以没给出的边却是可以使用的,也就是说本题实际上是在原图的反图上求一个环,即在每两个可以坐在相邻位置的小朋友连一条边,否则不连。使得该环包含所有顶点,即Hamilton回路。

由于有2n个小朋友,且每个小朋友的敌人最多n-1个,所以,每个小朋友可以一起与座的小朋友最少有n+1个,即度数>=n+1,所以任意两个小朋友度数之和d(u)+d(v)>=2n+2
> 2n,所以Hamilton回路存在。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
#define N 407

int vis[N],mp[N][N],ans[N];
int n,m;

void init()
{
for(int i=0;i<=n;i++)
{
for(int j=0;j<=n;j++)
{
if(i == j)
mp[i][j] = 0;
else
mp[i][j] = 1;
}
}
memset(ans,0,sizeof(ans));
}
void reverse(int ans[N],int s,int t) //将ans数组中s到t的部分倒置
{
int tmp;
while(s < t)
{
swap(ans[s],ans[t]);
s++;
t--;
}
}

void Hamilton()
{
int s = 1,t; //初始化s取1号点
int k = 2;
int i,j,w,tmp;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
{
if(mp[s][i])
break;
}
t = i; //取任意连接s的点为t
vis[s] = vis[t] = 1;
ans[0] = s;
ans[1] = t;
while(1)
{
//从t向外扩展
while(1)
{
for(i=1;i<=n;i++)
{
if(mp[t][i] && !vis[i])
{
ans[k++] = i;
vis[i] = 1;
t = i;
break;
}
}
if(i > n)
break;
}
//将当前得到的序列倒置,s和t互换,从t继续扩展,相当于在原来的序列上从s扩展
w = k - 1;
i = 0;
reverse(ans,i,w);
swap(s,t);
//从新的t向外扩展,相当于在原来的序列上从s向外扩展
while(1)
{
for(i=1;i<=n;i++)
{
if(mp[t][i] && !vis[i])
{
ans[k++] = i;
vis[i] = 1;
t = i;
break;
}
}
if(i > n)
break;
}
if(!mp[s][t]) //如果s和t不相邻,进行调整
{
for(i=1;i<k-2;i++)
{
if(mp[ans[i]][t] && mp[s][ans[i+1]]) //取序列中一点i,使得ans[i]与t相连接且ans[i+1]与s相连
break;
}
//将从ans[i+1]到t部分的ans[]倒置
w = k - 1;
i++;
t = ans[i];
reverse(ans,i,w);
}
//如果当前s和t相连
if(k == n) //如果当前序列中包含n个元素,算法结束
return;
//当前序列中的元素个数小于n,寻找点ans[i],使得ans[i]与ans[]外一点相连
for(j=1;j<=n;j++)
{
if(vis[j])
continue;
for(i=1;i<k-2;i++)
if(mp[ans[i]][j])
break;
if(mp[ans[i]][j])
break;
}
s = ans[i-1];
t = j;
reverse(ans,0,i-1); //将ans[]中s到ans[i-1]部分的ans[]倒置
reverse(ans,i,k-1); //将ans[]中ans[i]到t的部分倒置
ans[k++] = j; //将点j加入到ans[]的尾部
vis[j] = 1;
}
}

int main()
{
int i,j;
int a,b;
while(scanf("%d%d",&n,&m)!=EOF && (n||m))
{
n *= 2;
init();
for(i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
mp[a][b] = mp[b][a] = 0; //建立反图
}
Hamilton();
printf("%d",ans[0]);
for(i=1;i<n;i++)
printf(" %d",ans[i]);
printf("\n");
}
return 0;
}

Hamilton回路的判定与构造,布布扣,bubuko.com

时间: 2024-10-10 15:39:48

Hamilton回路的判定与构造的相关文章

Hamilton回路 旅行商TSP问题 /// dp oj1964

题目大意: 给出一个n个顶点的无向图,请寻找一条从顶点0出发,遍历其余顶点一次且仅一次.最后回到顶点0的回路--即Hamilton回路. Input 多测试用例.每个测试用例: 第一行,两个正整数 n 和 e ,0 < n ≤ 21 ,n < e < n×n/2 ,表示该无向图的顶点个数,以及边的数量.顶点编号是0-n-1 第二行至e+1行,每行3个非负整数 u , v 和 w ,分别表示顶点u与顶点v之间有一条边,其权值为w . Output 如果存在多条Hamilton回路,请输出长

这个是置顶的标题(`?ω?&#180;)

寒假几天的集训来看,自己不会的算法还是很多_(:зゝ∠)_,还有一些暑假学的忘记了,想想整个大二上学期,也就学了一个网络流,一个AC自动机,一个后缀数组.寒假时间还是很充裕的,适合研究几个算法. 就目前来看,自己唯一有优势的地方是代码能力稍微好一些,其他很多地方还是不如别人... [下面的完整知识目录来自这里@whatbeg] 1: 高级数据结构(17) 并查集,线段树,树状数组,KMP,字典树(Trie),左偏树(可合并堆),单调队列,优先队列,AC自动机,后缀树/数组,二叉堆,伸展树,Tre

哈密尔顿道路与哈密尔顿回路

简介 1857年爱尔兰数学家哈密尔顿发明了"周游世界"玩具,用一个正十二面体的20个顶点表示世界上20个大城市,30条棱代表这些城市之间的道路.要求游戏者从任意一个城市(即顶点)出发,延棱行走经过每个城市一次且只经过一次,最终返回出发地.哈密尔顿将此问题称为周游世界问题,并且作了肯定的回答. 以下是一种走法 与之等价的可以做成平面图,按这个编号走是可行的 哈密尔顿道路与哈密尔顿回路 哈密尔顿道路:通过图G中每个顶点一次且仅一次的道路称作该图的一条哈密尔顿道路. 哈密尔顿回路:通过图G中

Hamilton问题的一些假做法和一种真做法

Hamilton问题 Hamilton回路/路是指从某个点出发,不重复的经过每个点,最后回到起点/随便停留的路. 从刚开始学OI买了信息学一本通,这个问题就常常出现.由于它题面的简洁性,看起来无比优美的性质和欧拉回路的高度相似性,它看起来很像一个好算法,可惜,这是一个NP-hard问题. 因为可能会有不明真相的人点进来看,我首先介绍一下一种真实的做法:状压dp,似乎也不需要介绍了,当然搜索也是可以的,总之都是指数级算法了. 但是它却如幽灵一般给人解决它的希望...见到这种事情已经很多次了,今天决

【转】编程词汇

很实用的编程英语词库,共收录一千五百余条词汇. 第一部分: application 应用程式 应用.应用程序 application framework 应用程式框架.应用框架 应用程序框架 architecture 架构.系统架构 体系结构 argument 引数(传给函式的值).叁见 parameter 叁数.实质叁数.实叁.自变量 array 阵列 数组 arrow operator arrow(箭头)运算子 箭头操作符 assembly 装配件 assembly language 组合语

【转】Java 专业词汇

原址:http://blog.csdn.net/xiaojunjuns1/article/details/52729861 abstract (关键字)             抽象 ['.bstr.kt] access                            vt.访问,存取 ['.kses]'(n.入口,使用权) algorithm                     n.算法 ['.lg.riem] annotation                     [Java

Java基础常见英语词汇

(转自http://www.jianshu.com/p/2743fe834166) Java基础常见英语词汇(共70个) ['?bd?ekt] ['?:rientid]导向的 ['pr??ɡr?m??]编程OO: object-oriented ,面向对象 OOP: object-oriented programming,面向对象编程 [d?'vel?pm?nt][k?t]工具箱 ['v??tj??l]虚拟的JDK:Java development kit, java开发工具包 JVM:java

NOIP算法总结

前言 离NOIP还有一个星期,匆忙的把寒假整理的算法补充完善,看着当时的整理觉得那时还年少.第二页贴了几张从贴吧里找来的图片,看着就很热血的.旁边的同学都劝我不要再放PASCAL啊什么的了,毕竟我们的下一级直接学C++.即便我本人对C++也是赞赏有加,不过PASCAL作为梦的开始终究不能忘记.不像机房中其余的OIERS,我以后并不想学计算机类的专业.当年来学这个竞赛就是为了兴趣,感受计算机之美的.经过时迁,计划赶不上变化,现在尚处于迷茫之中,也很难说当时做的决定是对是错.然而我一直坚信迷茫的时候

编程词汇

application 应用程式 应用.应用程序 application framework 应用程式框架.应用框架 应用程序框架 architecture 架构.系统架构 体系结构 argument 引数(传给函式的值).叁见 parameter 叁数.实质叁数.实叁.自变量 array 阵列 数组 arrow operator arrow(箭头)运算子 箭头操作符 assembly 装配件 assembly language 组合语言 汇编语言 assert(ion) 断言 assign 指