哈密顿回路的算法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了哈密顿回路的算法相关的知识,希望对你有一定的参考价值。

哈密顿路径问题在上世纪七十年代初,终于被证明是“NP完备”的。据说具有这样性质的问题,难于找到一个有效的算法。实际上对于某些顶点数不到100的网络,利用现有最好的算法和计算机也需要比较荒唐的时间(比如几百年)才能确定其是否存在一条这样的路径。
从图中的任意一点出发,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。
要满足两个条件:
⒈封闭的环
⒉是一个连通图,且图中任意两点可达
经过图(有向图或无向图)中所有顶点一次且仅一次的通路称为哈密顿通路。
经过图中所有顶点一次且仅一次的回路称为哈密顿回路。
具有哈密顿回路的图称为哈密顿图,具有哈密顿通路但不具有哈密顿回路的图称为半哈密顿图。
平凡图是哈密顿图。
⒊若以1到2、2到3、3到4、4到5、5到1,为计数规律,则各点均出现两次;这种判断方法在计算机编程运算中显得尤为重要,其会精简很多运算过程。
⒋新出炉,有待检测的代码如下:
%-------输入的数据的原数据参照
% v1 v2 v3 v4 v5
%v1 0 20 1 11 2
%v2 0 0 9 1 3
%v3 0 0 0 13 8
%v4 0 0 0 0 6
%v5 0 0 0 0 0
%以上为输入数据的原数据参照
%建议所计算的数据矩阵长度为5,不会产生bug,且不会对任何计算机造成计算负担
%输入数据矩阵长度可以超过5,但是最初计算出的n个最小值中,重复次数超过2的点的种类只允许为一种
a=[0 20 1 11 2
0 0 9 1 3
0 0 0 13 8
0 0 0 0 6
0 0 0 0 0];
l=length(a)
s1=inf
zp=inf
n2=1
f=a
f(a==0)=inf
b=zeros(l)
i1=0
while i1<=l-1
[r c]=find(f==min(min(f)))
b(r⑴,c⑴)=f(r⑴,c⑴)
f(r⑴,c⑴)=inf
i1=i1+1
end
f1=f
[rz cz]=find(b>0)
pathz=[rz cz]
pz=[rz;cz]
p2z=zeros(2*l,1)
i2z=1
n2z=0
while i2z<=2*l
[r2z c2z]=find(pz==pz(i2z,1))
k1z=size(r2z)
if k1z(1,1)>2
p2z(r2z,1)=pz(r2z,1)
n2z=n2z+1
end
i2z=i2z+1
end
if n2z==2
HHL=b
zp=sum(sum(b))
else
while min(min(f1))~=inf
if n2>2
b=snh
end
[r1 c1]=find(b>0)
path1=[r1 c1]
p1=[r1;c1]
p2=zeros(2*l,1)
i2=1
n2=0
while i2<=2*l
[r2 c2]=find(p1==p1(i2,1))
k1=size(r2)
if k1(1,1)>2
p2(r2,1)=p1(r2,1)
n2=n2+1
end
i2=i2+1
end
[r3 c3]=find(p2>0)
p3=zeros(l,2)
i3=0
while i3<=n2-1
if r3⑴<=l
p3(r3⑴,:)=path1(r3⑴,:)
else
p3(r3⑴-l,:)=path1(r3⑴-l,:)
end
r3⑴=[]
i3=i3+1
end
p3(p3==0)=[]
p3=reshape(p3,n2,2)
p8=p2
[r8 c8]=find(p8>0)
p9=p8
r9=r8
i4=1
while i4<=n2
f1(p9(r9⑴,1),:)=inf
f1(:,p9(r9⑴,1))=inf
r9⑴=[]
i4=i4+1
end
[r4 c4]=find(f1==min(min(f1)))
f1(r4,c4)=inf
b1=b
b1(r4,c4)=a(r4,c4)
i5=1
p4=p3
while i5<=n2
b1=b
b1(r4⑴,c4⑴)=a(r4⑴,c4⑴)
b1(p4(1,1),p4(1,2))=0
p4(1,:)=[]
[r5 c5]=find(b1>0)
p5=[r5;c5]
i6=1
n6=0
while i6<=2*l
[r6 c6]=find(p5==p5(i6,1))
k6=size(r6)
if k6(1,1)>2
n6=n6+1
end
i6=i6+1
end
if n6>2
if sum(sum(b1))<s1
snh=[]
s1=sum(sum(b1))
snh=b1
end
else
if sum(sum(b1))<zp
HHL=[]
zp=sum(sum(b1))
HHL=b1
end
end
i5=i5+1
end
end
[rs cs]=find(HHL>0)
minpaths=[rs cs]
journeys=zp
注:这段代码采用分支定界法作为编写程序的依据,因此代码依旧局限在算法上;而且代码的使用对所要计算的数据是有要求的,如下:
⒈只要数据在开始计算出的n个最小值中,其重复次数超过2次的点的种类只能为一种,例如:代码段中的数据五个最小值中其重复次数超过2次的点只有v5。
⒉数据矩阵格式要求:只允许为上三角矩阵,不支持全矩阵以及下三角矩阵的运算。
⒊代码扩展方法请使用者独立思考,不唯一。
⒋运算数据扩展方法,请使用者独立思考,不唯一。
⒌此代码为本人毕设的附加产品,不会对使用此代码者,因理解不当或使用不当而造成的任何不良后果,付出任何责任。
⒍代码仅供交流。

参考技术A

哈密顿回路的算法是指:

在图论中是指含有哈密顿回路的图,闭合的哈密顿路径称作哈密顿回路。

哈密顿图(哈密尔顿图)(英语:Hamiltonian path,或Traceable path)是一个无向图,由天文学家哈密顿提出,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。

这个问题和著名的七桥问题的不同之处在于,过桥只需要确定起点,而不用确定终点。哈密顿问题寻找一条从给定的起点到给定的终点沿 途恰好经过所有其他城市一次的路径。

参考技术B /*数据的存储结构为邻接多重表,解题的思路是深度优先递归再配合回溯算,特别注意的是对顶点和边的访问、禁用、还原、进栈、出栈等操作,因本人才C语言几个月代码不够规范,代码可行性还待检验,仅供学习交流使用,如有需改善或未考虑到的细节请各位大神指出!*/
#include<stdio.h>
#include<windows.h>
#include<string.h>
#include<stdlib.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAX_VER_NUM 11//顶点的最大数
#define MAX_ARC_NUM 22//边的最大数
typedef char VertexType;
typedef int Status;
typedef struct EdgeInfo

VertexType v1;
VertexType v2;
int weight;
EdgeInfo;
typedef struct ArcBox//边所包含的信息

int iver;
struct ArcBox *ilink;
int jver;
struct ArcBox *jlink;
int weight;//权值
int mark;
char *info;
ArcBox;
typedef struct VerBox//顶点所包含的信息

VertexType data;//顶点值
ArcBox *firstedge;//指向邻接点(边所包含的信息)
VerBox;
typedef struct Graph

int vernum;//顶点总个数
int arcnum;//边的总个数
VerBox vertexs[MAX_VER_NUM];//顶点信息
Graph;
typedef struct StackData//栈中可存放的数据

VertexType data;
int lenght;
struct StackData *pnext;
StackData;
typedef struct Stack//栈用于存放已访问过的顶点

struct StackData *ptop;
struct StackData *pbottom;
STNODE;
typedef struct Stack_Arc//存方已访问过的边及顶点

ArcBox *p[MAX_ARC_NUM];
int v_num[MAX_ARC_NUM];
SANode;
int Visited[MAX_VER_NUM];//标记顶点是否被访问过
EdgeInfo Data[MAX_ARC_NUM]='A','B',324,'A','J',419,'A','K',328,'A','D',241,'A','C',556,'A','F',703,'A','G',521,'B','G',391,
/**************************/'B','H',230,'B','I',356,'B','J',220,'C','F',642,'C','E',337,'D','F',829,'D','K',334,'E','F',581,
/**************************/'E','G',1254,'F','G',887,'G','H',242,'H','I',249,'I','J',713,'J','K',398;//边及权值
int Count=0;//记可走边的总数
STNODE Stack;//存放已访问过
SANode Store_Arc_Ver;//存放弧的信息及顶点信息
int LAV=-1,ALL=0;
int Short_Len=1000000,Short_Load=0;//存放最断最路经
void CreateGraph(Graph **G);//创建图
int LocateVer(Graph G,VertexType v);//查找顶点v在图中的位置
void ShowAdjInfo(Graph *G);//查看邻接点信息
int FirstAdjVer(Graph *G,int v,ArcBox **u);//第一邻接点
int NextAdjVer(Graph *G,int v,int w,ArcBox **u);//下一邻接点
void NAV(ArcBox *p,int *n,int v,int w,ArcBox **u);//递归查找下一邻接点
void InitArcBox_mark(ArcBox *p);//初始化mark的值
void DFSTraverse(Graph *G);//深度优先遍历图
void DFST(Graph *G,int v);//剃归深度优先遍历
void InitStack(void);//初始化栈
void Push(VertexType c);//数据进栈
void Pop(void);//出栈
void PrintfArc(ArcBox *p);//打印弧的信息
Status IsAdj(int *len,VertexType v);//判断栈顶的点是否与A为邻接点
int main()

Graph *G=NULL;
CreateGraph(&G);
printf("顶点的邻接表:\n");
ShowAdjInfo(G);printf("\n\n");
printf("可走路径结果:\n");
DFSTraverse(G);printf("\n");
printf("可走路径总数:%d条;最短路径为:路径%d,长度为:%d\n\n",ALL,Short_Load,Short_Len);
return 0;

void CreateGraph(Graph **G)//创建图

int i,j,k,w;
char v1,v2;
ArcBox *pnew;
(*G)=(Graph *)malloc(1*sizeof(Graph));
if((*G)==NULL)

printf("动态内存分配失败,程序终止!\n");
exit(-1);

(*G)->arcnum=MAX_ARC_NUM;
(*G)->vernum=MAX_VER_NUM;
for(i=0;i<(*G)->vernum;i++)

(*G)->vertexs[i].data='A'+i;
(*G)->vertexs[i].firstedge=NULL;

for(k=0;k<(*G)->arcnum;k++)

v1=Data[k].v1;
v2=Data[k].v2;
w=Data[k].weight;
i=LocateVer((**G),v1);
j=LocateVer((**G),v2);
if(i>=0&&j>=0)

pnew=(ArcBox *)malloc(1*sizeof(ArcBox));
if(pnew==NULL)

printf("动态内存分配失败,程序终止!\n");
exit(-1);

pnew->iver=i;
pnew->jver=j;
pnew->weight=w;
pnew->mark=FALSE;
pnew->ilink=(*G)->vertexs[i].firstedge;
pnew->jlink=(*G)->vertexs[j].firstedge;
(*G)->vertexs[i].firstedge=pnew;
(*G)->vertexs[j].firstedge=pnew;

else

printf("注意:*****顶点%c不存在!*****\n",i<0?v1:v2);


return;

int LocateVer(Graph G,VertexType v)//查找顶点v在图中的位置

int i,result=-1;
for(i=0;i<MAX_VER_NUM;i++)

if(G.vertexs[i].data==v)

result=i;
break;


return result;

void ShowAdjInfo(Graph *G)//查看邻接点信息

int v,w;
ArcBox *u;
for(v=0;v<G->vernum;v++)

printf("[%d|%c]",v,G->vertexs[v].data);
for(w=FirstAdjVer(G,v,&u);w>=0;w=NextAdjVer(G,v,w,&u))

printf("->[%d|%c|%d]",w,G->vertexs[w].data,u->weight);

InitArcBox_mark(G->vertexs[v].firstedge);
printf("\n");



int FirstAdjVer(Graph *G,int v,ArcBox **u)//第一邻接点

int w=-1;
ArcBox *p;
p=G->vertexs[v].firstedge;
(*u)=p;
if(v==p->iver)

w=p->jver;
p->mark=TRUE;

else if(v==p->jver)

w=p->iver;
p->mark=TRUE;

return w;

int NextAdjVer(Graph *G,int v,int w,ArcBox **u)//下一邻接点

int n=-1;
ArcBox *p;
(*u)=NULL;
p=G->vertexs[v].firstedge;
NAV(p,&n,v,w,&(*u));
return n;


void NAV(ArcBox *p,int *n,int v,int w,ArcBox **u)//递归查找下一邻接点

if(p->mark==FALSE && (p->iver==v ||p->jver==v))

(*u)=p;
if(p->iver==v)

*n=p->jver;p->mark=TRUE;

else if(p->jver==v)

*n=p->iver;p->mark=TRUE;

else printf("下一邻接点数据出错,请检查!\n");

else

if(p->ilink!=NULL && *n==-1)

NAV(p->ilink,n,v,w,&(*u));

if(p->jlink!=NULL && *n==-1)

NAV(p->jlink,n,v,w,&(*u));


return;

void InitArcBox_mark(ArcBox *p)//初始化mark的值

p->mark=FALSE;
if(p->ilink!=NULL)

InitArcBox_mark(p->ilink);

if(p->jlink!=NULL)

InitArcBox_mark(p->jlink);

return;

void DFSTraverse(Graph *G)//深度优先遍历图

int v;
for(v=0;v<G->vernum;v++)

Visited[v]=FALSE;
InitArcBox_mark(G->vertexs[v].firstedge);

InitStack();
DFST(G,0);
return;

void DFST(Graph *G,int v)//剃归深度优先遍历

int w=-1,flag=1,i=0,enter=1,len=0;
ArcBox *u;//邻接点
StackData *p;
Visited[v]=TRUE;
Count++;
Push(G->vertexs[v].data);
if(Count==11&&IsAdj(&len,Stack.ptop->data)==1)

ALL++;
printf("路径%-2d:",ALL);
printf("A");
p=Stack.ptop;
len=len+p->lenght;
if(Short_Len>len) Short_Load=ALL,Short_Len=len;
while(p!=Stack.pbottom)

printf("->%c",p->data);
p=p->pnext;

printf(" 总长度为:%d",len);
printf("\n");

for(w=FirstAdjVer(G,v,&u);w>=0;w=NextAdjVer(G,v,w,&u))

enter=1;
for(i=0;i<=LAV;i++)

if(Store_Arc_Ver.p[i]==u)

enter=0;
break;


if(enter==1)

Store_Arc_Ver.p[++LAV]=u;
Store_Arc_Ver.v_num[LAV]=v;

if(Visited[w]==FALSE)

DFST(G,w);
Visited[w]=FALSE;
Count--;
Pop();


for(LAV;Store_Arc_Ver.v_num[LAV]==v&&LAV>=0;)//还原当前顶点边的状态并出栈

Store_Arc_Ver.p[LAV]->mark=FALSE;
Store_Arc_Ver.p[LAV]=NULL;
LAV--;


void InitStack(void)//初始化栈

Stack.pbottom=Stack.ptop=(StackData *)malloc(1*sizeof(StackData));
Stack.pbottom->pnext=NULL;
return;

void Push(VertexType c)//数据进栈

StackData *pnew;
char v1,v2;
int i;
pnew=(StackData *)malloc(1*sizeof(StackData));
pnew->data=c;
if(c=='A')

pnew->lenght=0;

else

v1=c;
v2=Stack.ptop->data;
for(i=0;i<MAX_ARC_NUM;i++)

if((v1==Data[i].v1 || v1==Data[i].v2) && (v2==Data[i].v1 || v2==Data[i].v2))

pnew->lenght=Stack.ptop->lenght+Data[i].weight;



pnew->pnext=Stack.ptop;
Stack.ptop=pnew;
return;

void Pop(void)

StackData *p;
p=Stack.ptop;
Stack.ptop=p->pnext;
free(p);

void PrintfArc(ArcBox *p)

printf("[%d|%d|%d|%d] ",p->iver,p->jver,p->mark,p->weight);
if(p->ilink!=NULL)

PrintfArc(p->ilink);

if(p->jlink!=NULL)

PrintfArc(p->jlink);


Status IsAdj(int *len,VertexType v)//判断是栈顶的点是否与A为邻接点

int i;
for(i=0;i<MAX_ARC_NUM;i++)

if((Data[i].v1==v&&Data[i].v2=='A')||(Data[i].v1=='A'&&Data[i].v2==v))

*len=Data[i].weight;
return TRUE;
break;


return FALSE;

POJ 2438 哈密顿回路

Children‘s Dining
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 4730   Accepted: 754   Special Judge

Description

Usually children in kindergarten like to quarrel with each other. This situation annoys the child-care women. For instant, when diner time comes, a fierce conflict may break out when a certain couple of children sitting side by side who are hostile with each other. Although there aren‘t too many children dining at the same round table, but the relationship of "enemy" or "friend" may be very complex. The child-care women do come across a big problem. Now it is time for you to help them to figure out a proper arrangement of sitting, with which no two "enemy" children is adjacent. 

Now we assume that there are 2 * n children who sit around a big table, and that none has more than n - 1 "enemies".

Input

The input is consisted of several test blocks. For each block, the first line contains two integers n and m (1 <= n <= 200, 0 <= m <= n (n - 1)). We use positive integers from 1 to 2 * n to label the children dining round table. Then m lines followed. Each contains positive integers i and j ( i is not equal to j, 1 <= i, j <= 2 * n), which indicate that child i and child j consider each other as "enemy". In a input block, a same relationship isn‘t given more than once, which means that if "i j" has been given, "j i" will not be given. 

There will be a blank line between input blocks. And m = n = 0 indicates the end of input and this case shouldn‘t be processed.

Output

For each test block, if the proper arrangement exist, you should print a line with a proper one; otherwise, print a line with "No solution!".

Sample Input

1 0

2 2
1 2
3 4

3 6
1 2
1 3
2 4
3 5
4 6
5 6

4 12
1 2
1 3
1 4
2 5
2 6
3 7
3 8
4 8
4 7
5 6
5 7
6 8

0 0

Sample Output

1 2
4 2 3 1
1 6 3 2 5 4
1 6 7 2 3 4 5 8

 

解析 题目说保证 每个点的度数等于n所以 一定存在哈密顿回路 然后在他给出的图的补图上求一个哈密顿回路就可以了 套一个O(n*n)的板子。

 哈密顿图:存在哈密顿回路的图

哈密顿图的判定:

一: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)阶竞赛图一点存在哈密顿通路.

 

AC代码

  1 #include <stdio.h>
  2 #include <string.h>
  3 using namespace std;
  4 const int maxn = 500+10;
  5 bool visit[maxn];
  6 int mapp[maxn][maxn],ans[maxn];
  7 int n,m;
  8 inline void _reverse(int s, int t) //将数组anv从下标s到t的部分的顺序反向
  9 {
 10     int temp;
 11     while(s  < t)
 12     {
 13         temp = ans[s];
 14         ans[s] = ans[t];
 15         ans[t] = temp;
 16         s++;
 17         t--;
 18     }
 19 }
 20 void Hamilton(int n)
 21 {
 22     memset(visit,false,sizeof(visit));
 23     int s = 1, t;//初始化取s为1号点
 24     int ansi = 2;
 25     int i, j;
 26     int w;
 27     int temp;
 28     for(i = 1; i <= n; i++) if(mapp[s][i]) break;
 29     t = i;//取任意邻接与s的点为t
 30     visit[s] = visit[t] = true;
 31     ans[0] = s;
 32     ans[1] = t;
 33     while(true)
 34     {
 35         while(true) //从t向外扩展
 36         {
 37             for(i = 1; i <= n; i++)
 38             {
 39                 if(mapp[t][i] && !visit[i])
 40                 {
 41                     ans[ansi++] = i;
 42                     visit[i] = true;
 43                     t = i;
 44                     break;
 45                 }
 46             }
 47             if(i > n) break;
 48         }
 49         w = ansi - 1;//将当前得到的序列倒置,s和t互换,从t继续扩展,相当于在原来的序列上从s向外扩展
 50         i = 0;
 51         _reverse(i, w);
 52         temp = s;
 53         s = t;
 54         t = temp;
 55         while(true) //从新的t继续向外扩展,相当于在原来的序列上从s向外扩展
 56         {
 57             for(i = 1; i <= n; i++)
 58             {
 59                 if(mapp[t][i] && !visit[i])
 60                 {
 61                     ans[ansi++] = i;
 62                     visit[i] = true;
 63                     t = i;
 64                     break;
 65                 }
 66             }
 67             if(i > n) break;
 68         }
 69         if(!mapp[s][t]) //如果s和t不相邻,进行调整
 70         {
 71             for(i = 1; i < ansi - 2; i++)//取序列中的一点i,使得ans[i]与t相连,并且ans[i+1]与s相连
 72                 if(mapp[ans[i]][t] && mapp[s][ans[i + 1]])break;
 73             w = ansi - 1;
 74             i++;
 75             t = ans[i];
 76             _reverse(i, w);//将从ans[i +1]到t部分的ans[]倒置
 77         }//此时s和t相连
 78         if(ansi == n) return;//如果当前序列包含n个元素,算法结束
 79         for(j = 1; j <= n; j++) //当前序列中元素的个数小于n,寻找点ans[i],使得ans[i]与ans[]外的一个点相连
 80         {
 81             if(visit[j]) continue;
 82             for(i = 1; i < ansi - 2; i++)if(mapp[ans[i]][j])break;
 83             if(mapp[ans[i]][j]) break;
 84         }
 85         s = ans[i - 1];
 86         t = j;//将新找到的点j赋给t
 87         _reverse(0, i - 1);//将ans[]中s到ans[i-1]的部分倒置
 88         _reverse(i, ansi - 1);//将ans[]中ans[i]到t的部分倒置
 89         ans[ansi++] = j;//将点j加入到ans[]尾部
 90         visit[j] = true;
 91     }
 92 }
 93 int main()
 94 {
 95     while(scanf("%d%d",&n,&m)!=EOF)
 96     {
 97         if(n==0)break;
 98         n*=2;
 99         memset(mapp,0,sizeof(mapp));
100         for(int i=1;i<=n;i++)
101             for(int j=1;j<=n;j++)
102                 mapp[i][j]=i==j?0:1;
103         for(int i=0;i<m;i++)
104         {
105             int u,v;
106             scanf("%d%d",&u,&v);
107             mapp[u][v]=mapp[v][u]=0;
108         }
109         Hamilton(n);
110         printf("%d",ans[0]);
111         for(int i=1;i<=n-1;i++)
112         {
113             printf(" %d",ans[i]);
114         }
115         printf("
");
116     }
117 }

该算法的详细过程及哈密顿回路的其他知识

https://www.cnblogs.com/Ash-ly/p/5452580.html

 





以上是关于哈密顿回路的算法的主要内容,如果未能解决你的问题,请参考以下文章

哈密顿回路算法详解

小专题哈密顿回路

【离散数学】图论(四)哈密顿回路(Hamiltonian cycle)

hamilton路径-图论算法模板

POJ 2438 哈密顿回路

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