回溯法之装载问题

问题描述:

    一共有n个货物要装上两艘重量分别为c1和c2的轮船上,其中货物i的重量为Wi,且:

                        

    要求确定是否有一个合理的装载方案可将货物装上这两艘轮船。

采取策略:      
 (1)首先将第一艘轮船尽可能装满;
 (2)将剩余的集装箱装上第二艘轮船。将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,
     使该子集中集装箱重量之和最接近。由此可知,装载问题等价于以下特殊的0-1背包问题:

                              

算法设计:

  先考虑装载一艘轮船的情况,依次讨论每个集装箱的装载情况,共分为两种,要么装(1),要么不装(0),因此很明显其解空间树可以用子集树来表示。

  在算法Maxloading中,返回不超过c的最大子集和,但是并没有给出到达这个最大子集和的相应子集,稍后完善。

  在算法Maxloading中,调用递归函数Backtrack(1)实现回溯搜索。Backtrack(i)搜索子集树中的第i层子树。

  在算法Backtrack中,当i>n时,算法搜索到叶结点,其相应的载重量为cw,如果cw>bestw,则表示当前解优于当前的最优解,此时应该更新bestw。

  算法Backtrack动态地生成问题的解空间树。在每个结点处算法花费O(1)时间。子集树中结点个数为O(2^n),故Backtrack所需的时间为O(2^n)。另外Backtrack还需要额外的O(n)的递归栈空间。

代码实现:

#include <iostream>
using namespace std;
 
typedef int* pINT;
template<class Type>
class Loading{
public:
    friend Type MaxLoading(Type* w,int num ,Type C1,int* bestx );
    friend void SolveLoading(int C2,bool* x,int* w,int num);
 
    void Backtrack(int i);
 
    int num;/* 集装箱数目 */
    int * x;/* 当前解 */
    int * bestx;/* 当前最优解 */
 
    Type* w;/* 集装箱重量数组 */
    Type  C1;/* 第一艘船的容量 */
    Type cw;
    Type bestw;
    Type r;/* 剩余集装箱重量 */
};
 
template<class Type>
void Loading<Type>::Backtrack( int i )
{
    if( i > num){
        if ( cw > bestw ) {
            for (int i = 1; i <= num ; i++ ) {
                bestx[i] = x[i];
                bestw = cw;                
            }
        }
        return ;
    }
    r -= w[i];
    if ( cw+w[i] <= C1 ) {
        x[i] = 1;
        cw += w[i];
        Backtrack(i+1);
        cw -= w[i];
    }
 
    if ( cw+r > bestw ) {
        x[i] = 0;
        Backtrack(i+1);
    }
 
    r += w[i];
 
}
template<class Type>
 Type   MaxLoading( Type* w,int num ,Type C1,int* bestx )
{
    Loading<Type> X;
    X.x = new int[num+1];
    X.w = w;
    X.C1= C1;
    X.num = num;
    X.bestx = bestx;
    X.bestw = 0;
    X.cw = 0;
    X.r = 0;
    for (int i = 1; i <= num ; i++ ) {
        X.r += w[i];
    }
    X.Backtrack(1);
    delete[] X.x;
    return X.bestw;
 
}
template<class Type>
 void    SolveLoading( int C2,int* x,Type* w,int num )
 {
     int totalW = 0;
     int c1W = 0;/* 第一艘船总载重 */
     for (int i = 1; i <= num ; i++ ) {
         if ( x[i] == 1 ) {
             c1W += w[i];
         } 
         totalW += w[i];
     }
     if ( totalW-c1W > C2 ) {
         printf("没有合理的装载方案! :( ");
         return;
     }
 
     printf(" 装载方案如下:\n ");
     printf(" 第一艘船装 ");
     for (int i = 1; i <= num ; i++ ) {
         if ( x[i] == 1 ) {
             printf("%d ",i);
         } 
     }
     printf("\n总载重 %d \n",c1W);
 
 
     printf(" 第二艘船装 ");
     for (int i = 1; i <= num ; i++ ) {
         if ( ! x[i] ) {
             printf("%d ",i);
         } 
     }
     printf("\n总载重 %d \n",totalW-c1W);
 
}
 
int main(int argc,char* argv[]){
 
    int C1 = 0;
    int C2 = 0;
    int num = 0;
    int* x = NULL;
    int** m = NULL;
    int* w = NULL;
 
    printf("输入第一艘船最大载重量:");
    scanf("%d",&C1);
 
    printf("输入第二艘船最大载重量:");
    scanf("%d",&C2);
 
 
    printf("输入货物个数");
    scanf("%d",&num);
 
    x = new int[num+1];
    w = new int[num+1];
    m = new pINT[num+1];
    for (int i = 0; i < num+1 ; i++ ) {
        m[i] = new int[num+1];
    }
 
 
    printf("分别输入货物重量(回车结束):\n");
 
    for (int i = 1; i <= num ; i++ ) {
        scanf("%d",w+i);
    }
 
 
 
    MaxLoading(  w, num, C1, x );
 
    SolveLoading(C2, x, w, num);
 
    delete[] x;
    delete[] w;
    delete[] m;
    
    return 0;
}
 
View Code

实现结果:

参考:王晓东《算法设计与分析》第二版

          https://www.cnblogs.com/xymqx/p/3724356.html

posted @ 2020-11-18 11:25  Chen洋  阅读(1510)  评论(0编辑  收藏  举报