哈密尔顿回路(旅行售货员问题)

哈密尔顿回路(旅行售货员问题)

哈密顿图(哈密尔顿图)(英语:Hamiltonian path,或Traceable path)是一个无向图,由天文学家哈密顿提出,由指定的起点前往指定的终点,途中经过所有其他节点且只经过一次。在图论中是指含有哈密顿回路的图,闭合的哈密顿路径称作哈密顿回路(Hamiltonian cycle),含有图中所有顶点的路径称作哈密顿路径。
在这里插入图片描述

由来

天文学家哈密顿(William Rowan Hamilton) 提出,在一个有多个城市的地图网络中,寻找一条从给定的起点到给定的终点沿 途恰好经过所有其他城市一次的路径。
这个问题和著名的七桥问题的不同之处在于,过桥只需要确定起点,而不用确定终点。哈密顿问题寻找一条从给定的起点到给定的终点沿 途恰好经过所有其他城市一次的路径

算法

哈密顿路径问题在上世纪七十年代初,终于被证明是“NP完备”的。据说具有这样性质的问题,难于找到一个有效的算法。实际上对于某些顶点数不到100的网络,利用现有最好的算法和计算机也需要比较荒唐的时间(比如几百年)才能确定其是否存在一条这样的路径。
从图中的任意一点出发,路途中经过图中每一个结点当且仅当一次,则成为哈密顿回路。
要满足两个条件:
⒈封闭的环
⒉是一个连通图,且图中任意两点可达
经过图(有向图或无向图)中所有顶点一次且仅一次的通路称为哈密顿通路。
经过图中所有顶点一次且仅一次的回路称为哈密顿回路。
具有哈密顿回路的图称为哈密顿图,具有哈密顿通路但不具有哈密顿回路的图称为半哈密顿图。

问题

旅行售货员问题又称TSP问题,问题如下:某售货员要到若干个城市推销商品,已知各城市之间的路程(或旅费),他要选定一条从驻地出发,经过每个城市一遍最后回到驻地的路线,使总的路线(或总的旅费)最小。数学模型为给定一个无向图,求遍历每一个顶点一次且仅一次的一条回路,最后回到起点的最小花费。
输入要求:
输入的第一行为测试样例的个数T( T < 120 ),接下来有T个测试样例。每个测试样例的第一行是无向图的顶点数n、边数m( n < 12,m < 100 ),接下来m行,每行三个整数u、v和w,表示顶点u和v之间有一条权值为w的边相连。( 1 <= u < v <= n,w <= 1000 )。假设起点(驻地)为1号顶点。
输出要求:
对应每个测试样例输出一行,格式为"Case #: W",其中’#'表示第几个测试样例(从1开始计),W为TSP问题的最优解,如果找不到可行方案则输出-1。
样例输入:
2
5 8
1 2 5
1 4 7
1 5 9
2 3 10
2 4 3
2 5 6
3 4 8
4 5 4
3 1
1 2 10
样例输出:
Case 1: 36
Case 2: -1

代码

#include<iostream>  
#define N 100  
using namespace std;  
int n,m,w,      //图的顶点数和边数 
  graph[N][N],   //图的加权邻接矩阵 
  c=0,       //当前费用 
  bestc=-1,     //当前最优值 
  x[N],      //当前解 
  bestx[N];    //当前最优解 
void backtrack(int k);  
void swap(int &a,int &b);  
void swap(int &a,int &b)  
{  
  int temp=a;  
  a=b;  
  b=temp;  
}  
void backtrack(int k)  
{  
  if(k==n)  
  {  
    if( (c+graph[x[n-1]][x[n]]+graph[x[n]][1]<bestc||bestc==-1) && graph[x[n-1]][x[n]]!=-1 && graph[x[n]][1]!=-1 )  
    {  
      bestc=c+graph[x[n-1]][x[n]]+graph[x[n]][1];  
      for(int i=1;i<=n;i++)  
      {  
        bestx[i]=x[i];  
      }  
    }  
    return ;  
  }  
  else 
  {  
    for(int i=k;i<=n;i++)  
    {  
      if( graph[x[k-1]][x[i]]!=-1 && (c+graph[x[k-1]][x[i]]<bestc || bestc==-1))  
      {  
        swap(x[i],x[k]);  
        c+=graph[x[k-1]][x[k]];  
        backtrack(k+1);  
        c-=graph[x[k-1]][x[k]];  
        swap(x[i],x[k]);  
      }  
    }  
  }  
}  
int main(void) 
{ 
  int i,j,tmp=1,testNum; 
  cin>>testNum; 
  while(tmp<=testNum) 
  { 
    cin>>n>>m; 
    for(i=1;i<=n;i++) 
    for(j=1;j<=n;j++) 
    graph[i][j]=-1; 
    for(int k=1;k<=m;k++) 
    { 
      cin>>i>>j>>w; 
      graph[i][j]=w; 
      graph[j][i]=w; 
    } 
    for(i=1;i<=n;i++) 
    { 
      x[i]=i; 
      bestx[i]=i; 
    } 
    backtrack(2); 
    cout<<"Case "<<tmp<<": "<<bestc<<endl; 
    bestc=-1; 
    c=0; 
      
    tmp++; 
  }   
  return 0; 
}
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值