问题描述
某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。
算法思路
旅行售货员问题的解空间可以组织成一棵树,从树的根结点到任一叶结点的路径定义了图的一条周游路线。旅行售货员问题要在图G中找出费用最小的周游路线。路线是一个带权图。图中各边的费用(权)为正数。图的一条周游路线是包括V中的每个顶点在内的一条回路。周游路线的费用是这条路线上所有边的费用之和。
算法开始时创建一个最小堆,用于表示活结点优先队列。堆中每个结点的子树费用的下界lcost值是优先队列的优先级。接着算法计算出图中每个顶点的最小费用出边并用minout记录。如果所给的有向图中某个顶点没有出边,则该图不可能有回路,算法即告结束。如果每个顶点都有出边,则根据计算出的minout作算法初始化。
算法的while循环体完成对排列树内部结点的扩展。对于当前扩展结点,算法分2种情况进行处理:
1、首先考虑s=n-2的情形,此时当前扩展结点是排列树中某个叶结点的父结点。如果该叶结点相应一条可行回路且费用小于当前最小费用,则将该叶结点插入到优先队列中,否则舍去该叶结点。
2、当s<n-2时,算法依次产生当前扩展结点的所有儿子结点。由于当前扩展结点所相应的路径是x[0:s],其可行儿子结点是从剩余顶点x[s+1:n-1]中选取的顶点x[i],且(x[s],x[i])是所给有向图G中的一条边。对于当前扩展结点的每一个可行儿子结点,计算出其前缀(x[0:s],x[i])的费用cc和相应的下界lcost。当lcost<bestc时,将这个可行儿子结点插入到活结点优先队列中。
算法中while循环的终止条件是排列树的一个叶结点成为当前扩展结点。当s=n-1时,已找到的回路前缀是x[0:n-1],它已包含图G的所有n个顶点。因此,当s=n-1时,相应的扩展结点表示一个叶结点。此时该叶结点所相应的回路的费用等于cc和lcost的值。剩余的活结点的lcost值不小于已找到的回路的费用。它们都不可能导致费用更小的回路。因此已找到的叶结点所相应的回路是一个最小费用旅行售货员回路,算法可以结束。
算法结束时返回找到的最小费用,相应的最优解由数组v给出。
算法执行过程最小堆中元素变化过程如下:
{ }—{B}—{C,D,E}—{C,D,J,K}—{C,J,K,H,I}—{C,J,K,I,N}—{C,K,I,N,P}—{C,I,N,P,Q}—{C,N,P,Q,O}—{C,P,Q,O}—{C,Q,O}—{Q,O,F,G}—{Q,O,G,L}—{Q,O,L,M}—{O,L,M}—{O,M}—{M}—{ }
算法具体实现如下:
1、MinHeap2.h
- #include <iostream>
- template<class Type>
- class Graph;
- template<class T>
- class MinHeap
- {
- template<class Type>
- friend class Graph;
- public:
- MinHeap(int maxheapsize = 10);
- ~MinHeap(){delete []heap;}
- int Size() const{return currentsize;}
- T Max(){if(currentsize) return heap[1];}
- MinHeap<T>& Insert(const T& x);
- MinHeap<T>& DeleteMin(T &x);
- void Initialize(T x[], int size, int ArraySize);
- void Deactivate();
- void output(T a[],int n);
- private:
- int currentsize, maxsize;
- T *heap;
- };
- template <class T>
- void MinHeap<T>::output(T a[],int n)
- {
- for(int i = 1; i <= n; i++)
- cout << a[i] << " ";
- cout << endl;
- }
- template <class T>
- MinHeap<T>::MinHeap(int maxheapsize)
- {
- maxsize = maxheapsize;
- heap = new T[maxsize + 1];
- currentsize = 0;
- }
- template<class T>
- MinHeap<T>& MinHeap<T>::Insert(const T& x)
- {
- if(currentsize == maxsize)
- {
- return *this;
- }
- int i = ++currentsize;
- while(i != 1 && x < heap[i/2])
- {
- heap[i] = heap[i/2];
- i /= 2;
- }
- heap[i] = x;
- return *this;
- }
- template<class T>
- MinHeap<T>& MinHeap<T>::DeleteMin(T& x)
- {
- if(currentsize == 0)
- {
- cout<<"Empty heap!"<<endl;
- return *this;
- }
- x = heap[1];
- T y = heap[currentsize--];
- int i = 1, ci = 2;
- while(ci <= currentsize)
- {
- if(ci < currentsize && heap[ci] > heap[ci + 1])
- {
- ci++;
- }
- if(y <= heap[ci])
- {
- break;
- }
- heap[i] = heap[ci];
- i = ci;
- ci *= 2;
- }
- heap[i] = y;
- return *this;
- }
- template<class T>
- void MinHeap<T>::Initialize(T x[], int size, int ArraySize)
- {
- delete []heap;
- heap = x;
- currentsize = size;
- maxsize = ArraySize;
- for(int i = currentsize / 2; i >= 1; i--)
- {
- T y = heap[i];
- int c = 2 * i;
- while(c <= currentsize)
- {
- if(c < currentsize && heap[c] > heap[c + 1])
- c++;
- if(y <= heap[c])
- break;
- heap[c / 2] = heap[c];
- c *= 2;
- }
- heap[c / 2] = y;
- }
- }
- template<class T>
- void MinHeap<T>::Deactivate()
- {
- heap = 0;
- }
#include <iostream>
template<class Type>
class Graph;
template<class T>
class MinHeap
{
template<class Type>
friend class Graph;
public:
MinHeap(int maxheapsize = 10);
~MinHeap(){delete []heap;}
int Size() const{return currentsize;}
T Max(){if(currentsize) return heap[1];}
MinHeap<T>& Insert(const T& x);
MinHeap<T>& DeleteMin(T &x);
void Initialize(T x[], int size, int ArraySize);
void Deactivate();
void output(T a[],int n);
private:
int currentsize, maxsize;
T *heap;
};
template <class T>
void MinHeap<T>::output(T a[],int n)
{
for(int i = 1; i <= n; i++)
cout << a[i] << " ";
cout << endl;
}
template <class T>
MinHeap<T>::MinHeap(int maxheapsize)
{
maxsize = maxheapsize;
heap = new T[maxsize + 1];
currentsize = 0;
}
template<class T>
MinHeap<T>& MinHeap<T>::Insert(const T& x)
{
if(currentsize == maxsize)
{
return *this;
}
int i = ++currentsize;
while(i != 1 && x < heap[i/2])
{
heap[i] = heap[i/2];
i /= 2;
}
heap[i] = x;
return *this;
}
template<class T>
MinHeap<T>& MinHeap<T>::DeleteMin(T& x)
{
if(currentsize == 0)
{
cout<<"Empty heap!"<<endl;
return *this;
}
x = heap[1];
T y = heap[currentsize--];
int i = 1, ci = 2;
while(ci <= currentsize)
{
if(ci < currentsize && heap[ci] > heap[ci + 1])
{
ci++;
}
if(y <= heap[ci])
{
break;
}
heap[i] = heap[ci];
i = ci;
ci *= 2;
}
heap[i] = y;
return *this;
}
template<class T>
void MinHeap<T>::Initialize(T x[], int size, int ArraySize)
{
delete []heap;
heap = x;
currentsize = size;
maxsize = ArraySize;
for(int i = currentsize / 2; i >= 1; i--)
{
T y = heap[i];
int c = 2 * i;
while(c <= currentsize)
{
if(c < currentsize && heap[c] > heap[c + 1])
c++;
if(y <= heap[c])
break;
heap[c / 2] = heap[c];
c *= 2;
}
heap[c / 2] = y;
}
}
template<class T>
void MinHeap<T>::Deactivate()
{
heap = 0;
}
2、6d7.cpp
- //旅行员售货问题 优先队列分支限界法求解
- #include "stdafx.h"
- #include "MinHeap2.h"
- #include <iostream>
- #include <fstream>
- using namespace std;
- ifstream fin("6d7.txt");
- const int N = 4;//图的顶点数
- template<class Type>
- class Traveling
- {
- friend int main();
- public:
- Type BBTSP(int v[]);
- private:
- int n; //图G的顶点数
- Type **a, //图G的邻接矩阵
- NoEdge, //图G的无边标识
- cc, //当前费用
- bestc; //当前最小费用
- };
- template<class Type>
- class MinHeapNode
- {
- friend Traveling<Type>;
- public:
- operator Type() const
- {
- return lcost;
- }
- private:
- Type lcost, //子树费用的下届
- cc, //当前费用
- rcost; //x[s:n-1]中顶点最小出边费用和
- int s, //根节点到当前节点的路径为x[0:s]
- *x; //需要进一步搜索的顶点是x[s+1,n-1]
- };
- int main()
- {
- int bestx[N+1];
- cout<<"图的顶点个数 n="<<N<<endl;
- int **a=new int*[N+1];
- for(int i=0;i<=N;i++)
- {
- a[i]=new int[N+1];
- }
- cout<<"图的邻接矩阵为:"<<endl;
- for(int i=1;i<=N;i++)
- {
- for(int j=1;j<=N;j++)
- {
- fin>>a[i][j];
- cout<<a[i][j]<<" ";
- }
- cout<<endl;
- }
- Traveling<int> t;
- t.a = a;
- t.n = N;
- cout<<"最短回路的长为:"<<t.BBTSP(bestx)<<endl;
- cout<<"最短回路为:"<<endl;
- for(int i=1;i<=N;i++)
- {
- cout<<bestx[i]<<"-->";
- }
- cout<<bestx[1]<<endl;
- for(int i=0;i<=N;i++)
- {
- delete []a[i];
- }
- delete []a;
- a=0;
- return 0;
- }
- //解旅行员售货问题的优先队列式分支限界法
- template<class Type>
- Type Traveling<Type>::BBTSP(int v[])
- {
- MinHeap<MinHeapNode<Type>> H(1000);
- Type * MinOut = new Type[n+1];
- //计算MinOut[i] = 顶点i的最小出边费用
- Type MinSum = 0; //最小出边费用和
- for(int i=1; i<=n; i++)
- {
- Type Min = NoEdge;
- for(int j=1; j<=n; j++)
- {
- if(a[i][j]!=NoEdge && (a[i][j]<Min||Min==NoEdge))
- {
- Min = a[i][j];
- }
- }
- if(Min == NoEdge)
- {
- return NoEdge; //无回路
- }
- MinOut[i] = Min;
- MinSum += Min;
- }
- //初始化
- MinHeapNode<Type> E;
- E.x = new int[n];
- for(int i=0; i<n; i++)
- {
- E.x[i] = i+1;
- }
- E.s = 0; //根节点到当前节点路径为x[0:s]
- E.cc = 0; //当前费用
- E.rcost = MinSum;//最小出边费用和
- Type bestc = NoEdge;
- //搜索排列空间树
- while(E.s<n-1)//非叶结点
- {
- if(E.s == n-2)//当前扩展节点是叶节点的父节点
- {
- //再加2条边构成回路
- //所构成回路是否优于当前最优解
- if(a[E.x[n-2]][E.x[n-1]]!=NoEdge && a[E.x[n-1]][1]!=NoEdge
- && (E.cc+a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1]<bestc
- || bestc == NoEdge))
- {
- //费用更小的回路
- bestc = E.cc + a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1];
- E.cc = bestc;
- E.lcost = bestc;
- E.s++;
- H.Insert(E);
- }
- else
- {
- delete[] E.x;//舍弃扩展节点
- }
- }
- else//产生当前扩展节点的儿子节点
- {
- for(int i=E.s+1;i<n;i++)
- {
- if(a[E.x[E.s]][E.x[i]]!=NoEdge)
- {
- //可行儿子节点
- Type cc = E.cc + a[E.x[E.s]][E.x[i]];
- Type rcost = E.rcost - MinOut[E.x[E.s]];
- Type b = cc + rcost;//下界
- if(b<bestc || bestc == NoEdge)
- {
- //子树可能含有最优解
- //节点插入最小堆
- MinHeapNode<Type> N;
- N.x = new int[n];
- for(int j=0; j<n; j++)
- {
- N.x[j] = E.x[j];
- }
- N.x[E.s+1] = E.x[i];
- N.x[i] = E.x[E.s+1];
- N.cc = cc;
- N.s = E.s + 1;
- N.lcost = b;
- N.rcost = rcost;
- H.Insert(N);
- }
- }
- }
- delete []E.x;//完成节点扩展
- }
- if(H.Size() == 0)
- {
- break;
- }
- H.DeleteMin(E);//取下一扩展节点
- }
- if(bestc == NoEdge)
- {
- return NoEdge;//无回路
- }
- //将最优解复制到v[1:n]
- for(int i=0; i<n; i++)
- {
- v[i+1] = E.x[i];
- }
- while(true)//释放最小堆中所有节点
- {
- delete []E.x;
- if(H.Size() == 0)
- {
- break;
- }
- H.DeleteMin(E);//取下一扩展节点
- }
- return bestc;
- }
//旅行员售货问题 优先队列分支限界法求解
#include "stdafx.h"
#include "MinHeap2.h"
#include <iostream>
#include <fstream>
using namespace std;
ifstream fin("6d7.txt");
const int N = 4;//图的顶点数
template<class Type>
class Traveling
{
friend int main();
public:
Type BBTSP(int v[]);
private:
int n; //图G的顶点数
Type **a, //图G的邻接矩阵
NoEdge, //图G的无边标识
cc, //当前费用
bestc; //当前最小费用
};
template<class Type>
class MinHeapNode
{
friend Traveling<Type>;
public:
operator Type() const
{
return lcost;
}
private:
Type lcost, //子树费用的下届
cc, //当前费用
rcost; //x[s:n-1]中顶点最小出边费用和
int s, //根节点到当前节点的路径为x[0:s]
*x; //需要进一步搜索的顶点是x[s+1,n-1]
};
int main()
{
int bestx[N+1];
cout<<"图的顶点个数 n="<<N<<endl;
int **a=new int*[N+1];
for(int i=0;i<=N;i++)
{
a[i]=new int[N+1];
}
cout<<"图的邻接矩阵为:"<<endl;
for(int i=1;i<=N;i++)
{
for(int j=1;j<=N;j++)
{
fin>>a[i][j];
cout<<a[i][j]<<" ";
}
cout<<endl;
}
Traveling<int> t;
t.a = a;
t.n = N;
cout<<"最短回路的长为:"<<t.BBTSP(bestx)<<endl;
cout<<"最短回路为:"<<endl;
for(int i=1;i<=N;i++)
{
cout<<bestx[i]<<"-->";
}
cout<<bestx[1]<<endl;
for(int i=0;i<=N;i++)
{
delete []a[i];
}
delete []a;
a=0;
return 0;
}
//解旅行员售货问题的优先队列式分支限界法
template<class Type>
Type Traveling<Type>::BBTSP(int v[])
{
MinHeap<MinHeapNode<Type>> H(1000);
Type * MinOut = new Type[n+1];
//计算MinOut[i] = 顶点i的最小出边费用
Type MinSum = 0; //最小出边费用和
for(int i=1; i<=n; i++)
{
Type Min = NoEdge;
for(int j=1; j<=n; j++)
{
if(a[i][j]!=NoEdge && (a[i][j]<Min||Min==NoEdge))
{
Min = a[i][j];
}
}
if(Min == NoEdge)
{
return NoEdge; //无回路
}
MinOut[i] = Min;
MinSum += Min;
}
//初始化
MinHeapNode<Type> E;
E.x = new int[n];
for(int i=0; i<n; i++)
{
E.x[i] = i+1;
}
E.s = 0; //根节点到当前节点路径为x[0:s]
E.cc = 0; //当前费用
E.rcost = MinSum;//最小出边费用和
Type bestc = NoEdge;
//搜索排列空间树
while(E.s<n-1)//非叶结点
{
if(E.s == n-2)//当前扩展节点是叶节点的父节点
{
//再加2条边构成回路
//所构成回路是否优于当前最优解
if(a[E.x[n-2]][E.x[n-1]]!=NoEdge && a[E.x[n-1]][1]!=NoEdge
&& (E.cc+a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1]<bestc
|| bestc == NoEdge))
{
//费用更小的回路
bestc = E.cc + a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1];
E.cc = bestc;
E.lcost = bestc;
E.s++;
H.Insert(E);
}
else
{
delete[] E.x;//舍弃扩展节点
}
}
else//产生当前扩展节点的儿子节点
{
for(int i=E.s+1;i<n;i++)
{
if(a[E.x[E.s]][E.x[i]]!=NoEdge)
{
//可行儿子节点
Type cc = E.cc + a[E.x[E.s]][E.x[i]];
Type rcost = E.rcost - MinOut[E.x[E.s]];
Type b = cc + rcost;//下界
if(b<bestc || bestc == NoEdge)
{
//子树可能含有最优解
//节点插入最小堆
MinHeapNode<Type> N;
N.x = new int[n];
for(int j=0; j<n; j++)
{
N.x[j] = E.x[j];
}
N.x[E.s+1] = E.x[i];
N.x[i] = E.x[E.s+1];
N.cc = cc;
N.s = E.s + 1;
N.lcost = b;
N.rcost = rcost;
H.Insert(N);
}
}
}
delete []E.x;//完成节点扩展
}
if(H.Size() == 0)
{
break;
}
H.DeleteMin(E);//取下一扩展节点
}
if(bestc == NoEdge)
{
return NoEdge;//无回路
}
//将最优解复制到v[1:n]
for(int i=0; i<n; i++)
{
v[i+1] = E.x[i];
}
while(true)//释放最小堆中所有节点
{
delete []E.x;
if(H.Size() == 0)
{
break;
}
H.DeleteMin(E);//取下一扩展节点
}
return bestc;
}
程序运行结果如图: