【哈密顿图】算法分析
前言
这是一篇关于哈密顿图算法分析课的一项作业。
一、哈密顿图是什么?
哈密顿图(哈密尔顿图) ( 英语: Hamiltonianpath,或Traceablepath)是一个无向图,由天文学家哈密顿提出,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。从一张普通图的任意一点出发,路途中经过图中每一个结点当且仅当 一次,则成为哈密顿回路。
二、问题分析
哈密顿图是一个无向图,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。从一张普通图的任意一点出发,路途中经过图中每一个结点当且仅当 一次,则为哈密顿回路。
要求:随机生成无向图,且图中部分顶点间无通路。设计算法找到一条哈密顿回路。且采用多种方法解决。
方法:可以选用暴力枚举和回溯法解决。
三、代码
1.引入库
代码如下(示例):
#include<bits/stdc++.h>
using namespace std;
const int N=10; //顶点数
int E[N+1][N+1]; //邻接矩阵
int Q[N+1][N+1]; //加权邻接矩阵
int randnum[]={1,1,1,1,1,1,0,0,0,0};//设置随机数种子
int tot1=0,tot2=0; //用来记录找到的哈密顿回路数
//以下变量和数组用于方法二:回溯法
int arr[N+1]; //用来储存通路经过的顶点
int book[N+1]; //用来表示此数字有没有被用过,初始化为0表示没用过
2.完整代码
代码如下(示例):
#include<bits/stdc++.h>
using namespace std;
const int N=10; //顶点数
int E[N+1][N+1]; //邻接矩阵
int Q[N+1][N+1]; //加权邻接矩阵
int randnum[]={1,1,1,1,1,1,0,0,0,0};//设置随机数种子
int tot1=0,tot2=0; //用来记录找到的哈密顿回路数
//以下变量和数组用于方法二:回溯法
int arr[N+1]; //用来储存通路经过的顶点
int book[N+1]; //用来表示此数字有没有被用过,初始化为0表示没用过
void f1();
void f2(int step,int now);
int main()
{
//展示该图的基本信息
srand((unsigned)time(NULL));
cout<<"该图有"<<N<<"个顶点。"<<endl;
for(int i=1;i<=N;i++)
{
for(int j=i+1;j<=N;j++)
{
E[i][j]=E[j][i]=randnum[rand()%( sizeof(randnum)/sizeof(int) )];//随机为各顶点间添加无向边
if( E[i][j] ) Q[i][j]=Q[j][i]=rand()%20+1;
}
}
cout<<"该图的边所构成的邻接矩阵如下:"<<endl;
for(int i=0;i<=N;i++)
{
printf(i==0?"\t":"%d\t",i);
}
cout<<endl;
for(int i=1;i<=N;i++)
{
printf("%d\t",i);
for(int j=1;j<=N;j++)
{
printf("%d\t",E[i][j]);
}
cout<<endl<<endl<<endl;
}
int start1,end1,start2,end2;
//方法一:暴力枚举
start1=clock();
f1();
end1=clock();
if(tot1) cout<<"该图共有"<<tot1<<"条哈密顿回路"<<endl;
else cout<<"该图没有哈密顿回路"<<endl;
cout<<"方法一:暴力枚举所用时间为:"<<end1-start1<<"ms"<<endl;
cout<<endl<<endl<<endl;
//方法二:回溯法
start2=clock();
f2(1,1);
if(tot2) cout<<"该图共有"<<tot2<<"条哈密顿回路"<<endl;
else cout<<"该图没有哈密顿回路"<<endl;
end2=clock();
cout<<"方法二:回溯法所用时间为:"<<end2-start2<<"ms"<<endl;
}
void f1()
{
int V[N+2],num=1;
for(int i=1;i<=N;i++) V[i]=i;
V[N+1]=V[1];
for(int i=1;i<=N;i++) num*=E[ V[i] ][ V[i+1] ];//先对1到n这个排列进行验证
if(num)
{
tot1++;
// for(int i=1;i<=N+1;i++) printf(i==1?"%d":"--%d",V[i]);
// cout<<"是一条哈密顿回路"<<endl;
}
while(next_permutation(V+1,V+1+N))
{
if(V[1]!=1) break;//保证这条通路是从点1开始的
num=1;
for(int i=1;i<=N;i++) num*=E[ V[i] ][ V[i+1] ];//验证
if(num)
{
tot1++;
// for(int i=1;i<=N+1;i++) printf(i==1?"%d":"--%d",V[i]);
// cout<<"是一条哈密顿回路"<<endl;
}
}
}
void f2(int step ,int now)//step代表从顶点1出发到达的第step个点,now代表现在遍历到的顶点编号
{
// 输出条件:
// 1、找到一条哈密顿通路
// 2、验证该哈密顿通路的最后一个顶点是否与顶点1直接连接,即验证是否为哈密顿回路
if( step==N && E[now][1] )
{
tot2++;
// printf("1");
// for(int i=1;i<=N-1;i++)
// {
// printf("--%d",arr[i]);
// }
// printf("--1是一条哈密顿回路。\n");
}
for(int i=2;i<=N;i++)
{
if( E[now][i] && (!book[i]) )
{
book[i]=1; //先让他标记为1表示用过了
arr[step]=i; //放到盒子里去
f2(step+1,i); //继续搜索下一个地方
book[i]=0; //消除之前的标记
}
}
}
四、运行结果
例如:
总结
通过这个方法基本可以完成哈密顿图的实现。这次的课程作业,让我们组通过此次算法编程以及本学期的算法学习了解了一些计算机编程知识和技巧方法,掌握了一些计算机编程能力,这次课程设计也是首次对我们的个人编程能力的检验。可能不是很完美,但是也是一次对自己的检测。