回溯法之裝載問題


問題描述:

    一共有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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM