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

来源:互联网 发布:彩票中奖 知乎 编辑:程序博客网 时间:2024/06/11 05:59

    问题描述

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


    算法思路

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

[cpp] view plaincopyprint?
  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

[cpp] view plaincopyprint?
  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;}
     程序运行结果如图: