0038算法笔记——【分支限界法】旅行员售货问题

    问题描述

     某售货员要到若干城市去推销商品,已知各城市之间的路程(旅费),他要选定一条从驻地出发,经过每个城市一遍,最后回到驻地的路线,使总的路程(总旅费)最小。


    算法思路

     旅行售货员问题的解空间可以组织成一棵树,从树的根结点到任一叶结点的路径定义了图的一条周游路线。旅行售货员问题要在图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

  1. #include <iostream>   
  2.   
  3. template<class Type>  
  4. class Graph;  
  5.   
  6. template<class T>   
  7. class MinHeap   
  8. {   
  9.     template<class Type>  
  10.     friend class Graph;  
  11.     public:   
  12.         MinHeap(int maxheapsize = 10);   
  13.         ~MinHeap(){delete []heap;}   
  14.   
  15.         int Size() const{return currentsize;}   
  16.         T Max(){if(currentsize) return heap[1];}   
  17.   
  18.         MinHeap<T>& Insert(const T& x);   
  19.         MinHeap<T>& DeleteMin(T &x);   
  20.   
  21.         void Initialize(T x[], int size, int ArraySize);   
  22.         void Deactivate();   
  23.         void output(T a[],int n);  
  24.     private:   
  25.         int currentsize, maxsize;   
  26.         T *heap;   
  27. };   
  28.   
  29. template <class T>   
  30. void MinHeap<T>::output(T a[],int n)   
  31. {   
  32.     for(int i = 1; i <= n; i++)   
  33.     cout << a[i] << " ";   
  34.     cout << endl;   
  35. }   
  36.   
  37. template <class T>   
  38. MinHeap<T>::MinHeap(int maxheapsize)   
  39. {   
  40.     maxsize = maxheapsize;   
  41.     heap = new T[maxsize + 1];   
  42.     currentsize = 0;   
  43. }   
  44.   
  45. template<class T>   
  46. MinHeap<T>& MinHeap<T>::Insert(const T& x)   
  47. {   
  48.     if(currentsize == maxsize)   
  49.     {   
  50.         return *this;   
  51.     }   
  52.     int i = ++currentsize;   
  53.     while(i != 1 && x < heap[i/2])   
  54.     {   
  55.         heap[i] = heap[i/2];   
  56.         i /= 2;   
  57.     }   
  58.   
  59.     heap[i] = x;   
  60.     return *this;   
  61. }   
  62.   
  63. template<class T>   
  64. MinHeap<T>& MinHeap<T>::DeleteMin(T& x)   
  65. {   
  66.     if(currentsize == 0)   
  67.     {   
  68.         cout<<"Empty heap!"<<endl;   
  69.         return *this;   
  70.     }   
  71.   
  72.     x = heap[1];   
  73.   
  74.     T y = heap[currentsize--];   
  75.     int i = 1, ci = 2;   
  76.     while(ci <= currentsize)   
  77.     {   
  78.         if(ci < currentsize && heap[ci] > heap[ci + 1])   
  79.         {   
  80.             ci++;   
  81.         }   
  82.   
  83.         if(y <= heap[ci])   
  84.         {   
  85.             break;   
  86.         }   
  87.         heap[i] = heap[ci];   
  88.         i = ci;   
  89.         ci *= 2;   
  90.     }   
  91.   
  92.     heap[i] = y;   
  93.     return *this;   
  94. }   
  95.   
  96. template<class T>   
  97. void MinHeap<T>::Initialize(T x[], int size, int ArraySize)   
  98. {   
  99.     delete []heap;   
  100.     heap = x;   
  101.     currentsize = size;   
  102.     maxsize = ArraySize;   
  103.   
  104.     for(int i = currentsize / 2; i >= 1; i--)   
  105.     {   
  106.         T y = heap[i];   
  107.         int c = 2 * i;   
  108.         while(c <= currentsize)   
  109.         {   
  110.             if(c < currentsize && heap[c] > heap[c + 1])   
  111.                 c++;   
  112.             if(y <= heap[c])   
  113.                 break;   
  114.             heap[c / 2] = heap[c];   
  115.             c *= 2;   
  116.         }   
  117.         heap[c / 2] = y;   
  118.     }   
  119. }   
  120.   
  121. template<class T>   
  122. void MinHeap<T>::Deactivate()   
  123. {   
  124.     heap = 0;   
  125. }   
#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

  1. //旅行员售货问题 优先队列分支限界法求解    
  2. #include "stdafx.h"   
  3. #include "MinHeap2.h"   
  4. #include <iostream>   
  5. #include <fstream>    
  6. using namespace std;  
  7.   
  8. ifstream fin("6d7.txt");   
  9. const int N = 4;//图的顶点数     
  10.   
  11. template<class Type>  
  12. class Traveling  
  13. {  
  14.     friend int main();  
  15.     public:  
  16.         Type BBTSP(int v[]);  
  17.     private:  
  18.         int n;      //图G的顶点数   
  19.         Type **a,   //图G的邻接矩阵   
  20.         NoEdge,     //图G的无边标识   
  21.         cc,         //当前费用   
  22.         bestc;      //当前最小费用   
  23. };  
  24.   
  25. template<class Type>  
  26. class MinHeapNode  
  27. {  
  28.     friend Traveling<Type>;  
  29.     public:  
  30.         operator Type() const  
  31.         {  
  32.             return lcost;  
  33.         }  
  34.     private:  
  35.         Type lcost,     //子树费用的下届   
  36.                 cc,     //当前费用   
  37.                 rcost;  //x[s:n-1]中顶点最小出边费用和   
  38.         int s,          //根节点到当前节点的路径为x[0:s]   
  39.             *x;         //需要进一步搜索的顶点是x[s+1,n-1]   
  40. };  
  41.   
  42. int main()  
  43. {  
  44.     int bestx[N+1];  
  45.     cout<<"图的顶点个数 n="<<N<<endl;  
  46.   
  47.     int **a=new int*[N+1];  
  48.     for(int i=0;i<=N;i++)  
  49.     {  
  50.         a[i]=new int[N+1];  
  51.     }  
  52.   
  53.     cout<<"图的邻接矩阵为:"<<endl;  
  54.   
  55.     for(int i=1;i<=N;i++)  
  56.     {  
  57.         for(int j=1;j<=N;j++)  
  58.         {  
  59.             fin>>a[i][j];  
  60.             cout<<a[i][j]<<" ";  
  61.         }  
  62.         cout<<endl;  
  63.     }  
  64.   
  65.     Traveling<int> t;  
  66.     t.a = a;  
  67.     t.n = N;  
  68.   
  69.     cout<<"最短回路的长为:"<<t.BBTSP(bestx)<<endl;  
  70.     cout<<"最短回路为:"<<endl;  
  71.     for(int i=1;i<=N;i++)  
  72.     {  
  73.         cout<<bestx[i]<<"-->";  
  74.     }  
  75.     cout<<bestx[1]<<endl;  
  76.   
  77.     for(int i=0;i<=N;i++)  
  78.     {  
  79.         delete []a[i];  
  80.     }  
  81.     delete []a;  
  82.   
  83.     a=0;  
  84.     return 0;  
  85. }  
  86.   
  87. //解旅行员售货问题的优先队列式分支限界法   
  88. template<class Type>  
  89. Type Traveling<Type>::BBTSP(int v[])  
  90. {  
  91.     MinHeap<MinHeapNode<Type>> H(1000);  
  92.     Type * MinOut = new Type[n+1];  
  93.     //计算MinOut[i] = 顶点i的最小出边费用   
  94.     Type MinSum = 0; //最小出边费用和   
  95.     for(int i=1; i<=n; i++)  
  96.     {  
  97.         Type Min = NoEdge;  
  98.         for(int j=1; j<=n; j++)  
  99.         {  
  100.             if(a[i][j]!=NoEdge && (a[i][j]<Min||Min==NoEdge))  
  101.             {  
  102.                 Min  = a[i][j];  
  103.             }  
  104.         }  
  105.         if(Min == NoEdge)  
  106.         {  
  107.             return NoEdge;      //无回路   
  108.         }  
  109.         MinOut[i] = Min;  
  110.         MinSum += Min;  
  111.     }  
  112.   
  113.     //初始化   
  114.     MinHeapNode<Type> E;  
  115.     E.x = new int[n];  
  116.     for(int i=0; i<n; i++)  
  117.     {  
  118.         E.x[i] = i+1;  
  119.     }  
  120.     E.s = 0;        //根节点到当前节点路径为x[0:s]   
  121.     E.cc = 0;       //当前费用   
  122.     E.rcost = MinSum;//最小出边费用和   
  123.     Type bestc = NoEdge;  
  124.   
  125.     //搜索排列空间树   
  126.     while(E.s<n-1)//非叶结点   
  127.     {  
  128.         if(E.s == n-2)//当前扩展节点是叶节点的父节点   
  129.         {  
  130.             //再加2条边构成回路   
  131.             //所构成回路是否优于当前最优解   
  132.             if(a[E.x[n-2]][E.x[n-1]]!=NoEdge && a[E.x[n-1]][1]!=NoEdge  
  133.                 && (E.cc+a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1]<bestc  
  134.                 || bestc == NoEdge))  
  135.             {  
  136.                 //费用更小的回路   
  137.                 bestc = E.cc + a[E.x[n-2]][E.x[n-1]]+a[E.x[n-1]][1];  
  138.                 E.cc = bestc;  
  139.                 E.lcost = bestc;  
  140.                 E.s++;  
  141.                 H.Insert(E);  
  142.             }  
  143.             else  
  144.             {  
  145.                 delete[] E.x;//舍弃扩展节点   
  146.             }  
  147.         }  
  148.         else//产生当前扩展节点的儿子节点   
  149.         {  
  150.             for(int i=E.s+1;i<n;i++)  
  151.             {  
  152.                 if(a[E.x[E.s]][E.x[i]]!=NoEdge)  
  153.                 {  
  154.                     //可行儿子节点   
  155.                     Type cc = E.cc + a[E.x[E.s]][E.x[i]];  
  156.                     Type rcost = E.rcost - MinOut[E.x[E.s]];  
  157.                     Type b = cc + rcost;//下界   
  158.                     if(b<bestc || bestc == NoEdge)  
  159.                     {  
  160.                         //子树可能含有最优解   
  161.                         //节点插入最小堆   
  162.                         MinHeapNode<Type> N;  
  163.                         N.x = new int[n];  
  164.                         for(int j=0; j<n; j++)  
  165.                         {  
  166.                             N.x[j] = E.x[j];  
  167.                         }  
  168.                         N.x[E.s+1] = E.x[i];  
  169.                         N.x[i] = E.x[E.s+1];  
  170.                         N.cc = cc;  
  171.                         N.s = E.s + 1;  
  172.                         N.lcost = b;  
  173.                         N.rcost = rcost;  
  174.                         H.Insert(N);  
  175.                     }  
  176.                 }  
  177.             }  
  178.             delete []E.x;//完成节点扩展   
  179.         }  
  180.         if(H.Size() == 0)  
  181.         {  
  182.             break;  
  183.         }  
  184.         H.DeleteMin(E);//取下一扩展节点   
  185.     }  
  186.   
  187.     if(bestc == NoEdge)  
  188.     {  
  189.         return NoEdge;//无回路   
  190.     }  
  191.     //将最优解复制到v[1:n]   
  192.     for(int i=0; i<n; i++)  
  193.     {  
  194.         v[i+1] = E.x[i];  
  195.     }  
  196.   
  197.     while(true)//释放最小堆中所有节点   
  198.     {  
  199.         delete []E.x;  
  200.         if(H.Size() == 0)  
  201.         {  
  202.             break;  
  203.         }  
  204.         H.DeleteMin(E);//取下一扩展节点   
  205.     }     
  206.     return bestc;  
  207. }  
//旅行员售货问题 优先队列分支限界法求解 
#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;
}
     程序运行结果如图:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值