回溯算法之輪船貨箱裝載問題(最優裝載)


1. 問題描述:

  給定 n 個貨箱,貨箱 i 重為 wi ,船可以裝載的貨箱總重量為W。貨箱裝載問題是在不使船翻的前提下裝載盡可能多的貨箱。

2. 解空間:

  假設解可以由向量 (x1, x2, ... , xn) 表示, xi 屬於 {0, 1} , xi = 1 表示貨箱 i 被裝上船, xi = 0 表示貨箱 i 不裝上船。

3. 約束函數:

  令 cw(i) 表示到第 i 層的當前總重量,則約束函數為 C(i) = cw(i-1) + wi 

4. 限界函數:

  B(i) = C(i) + r(i),其中 r(i) 表示剩余貨箱的總重量

5. 剪枝:

  若 B(i) <= bestw,則停止搜索第 i 層及其下面的層,否則繼續搜索。其中,bestw 表示目前為止得到的最佳重量。

 

C 語言實現:

代碼參考自:https://www.xuebuyuan.com/2133367.html

// JJU_干干

/*
++++++++++++++++++++++++++++++++++++++++++++++++++++

一般解題步驟分為三步
一:針對所給問題,定義問題的解空間
二:確定易於搜索的解空間結構(一般為子集樹或者排列樹)
三:以深度優先的方式搜索解空間,並且在搜索過程中用減枝函數避免無效搜索
其中子集樹就是選一部分,比如0-1背包問題,裝載問題,
而排列樹就是選所有,只是順序不一樣,例如旅行商(郵遞員)問題

++++++++++++++++++++++++++++++++++++++++++++++++++++
*/

#include <stdio.h>
#include <stdlib.h>
#define NUM 100
int n;  //集裝箱數量
int W;  //輪船載重量
int w[NUM]; //集裝箱重量
int x[NUM]; //當前搜索的解向量
int r;  //剩余集裝箱重量
int cw; //當前輪船載重量
int bestw;  //最優載重
int bestx[NUM]; //最優解向量

void init()
{
    int i;
    bestw=0;  // 最優載重量初始化為0
    printf("請輸入各集裝箱的重量:");
    for( i=1;i<=n;i++)
    {
        scanf("%d",&w[i]);
        r+=w[i];
    }
}

//形參代表搜索第t層節點,從1開始
void BackTrack(int t)
{
    int i;
    //到達葉子節點, 此時就找到了一個可行解,然后就更新最優解向量和最優載重
    if(t>n)  
    {
        for(i=1;i<=n;i++)
        {
            bestx[i]=x[i];
        }
        bestw=cw;
    }
    else  // 未到達葉子節點
    {
        r-=w[t];    //先假設當前貨箱可以裝入,再判斷能不能裝入,因此這里要更新剩余集裝箱重量
 
        //沒有超出載重量
        if(cw+w[t]<=W) 
        {
            x[t]=1;  // 可以裝入,則賦值為1
            cw+=w[t];  // 更新當前裝入的總重量
            BackTrack(t+1);  // 遞歸進入下一貨箱, 只要不超在,就會一直深度搜索下去
            cw-=w[t];   //返回時要將當前載重量還原,即回溯
        }
    
        //如果當前載重量加上剩余集裝箱的重量沒有超過前一個最優裝載量的話就不用考慮了
        //此處即為剪枝函數,剪枝之前,至少有一個可行解已經產生
        if(cw+r>bestw)      
        {
            x[t]=0;     // 如果進入這個if 語句就說明,以該節點的子樹不能剪,但通過上一個if語句的回溯后,該節點進入另一個選擇,應賦值為0
            BackTrack(t+1);  //遞歸進入下一貨箱
        }
        r+=w[t];    //返回時還原剩余集裝箱重量,即回溯
    }
    return;
}

void print() // 打印函數
{
    int i;
    printf("最優載重:%d\n",bestw);
    printf("最佳解向量為:");
    for(i=1;i<=n;i++)
        printf("%d",bestx[i]);
    printf("\n即:");
    for(i=1;i<=n;i++)
    {
        if(bestx[i]==1)
            printf("\n第%d個貨箱(重量為:%d), 裝入輪船",i,w[i]);
        else
            printf("\n第%d個貨箱(重量為:%d), 不裝入輪船",i,w[i]);
    }
    printf("\n");
}

void main()
{
    printf("請輸入集裝箱的個數:");
    scanf("%d",&n);
    printf("請輸入輪船的載重量:");
    scanf("%d",&W);
    init();
    BackTrack(1);
    print();
    system("pause");
}

 

運行結果:

 

Python 實現:

# JJU_干干

def init():
    global bestw,cw,w,r,x
    bestw =0
    cw = 0
    x=[0]*100
    w= list(eval(input("請輸入各貨箱的重量:")))
    r = sum(w)


def BackTrack(t):
    global bestx,bestw,cw,r,x
    if t>(n-1):
        bestx = x
        bestw = cw
    else:
        r -=w[t]
        if (cw+w[t])<=W:
            cw += w[t]
            x[t] = 1
            BackTrack(t+1)
            cw -= w[t]

        if (cw+r) > bestw:
            x[t] = 0
            BackTrack(t+1)

        r += w[t]
    return

def print_func():
    global bestw,bestx,w,x,n
    print("最佳載重為:{0},最佳解向量:{1}".format(bestw,bestx[0:n]))
    print("即:")
    for i in range(n):
        if x[i] == 1:
            print("第{0}個貨箱(重量為:{1})裝入輪船".format(i+1,w[i]))
        else:
            print("第{0}個貨箱(重量為:{1})不裝入輪船".format(i + 1, w[i]))


if __name__ == '__main__':
    n = int(input("請輸入貨箱的個數:"))
    W = int(input("請輸入輪船的載重量:"))
    init()
    BackTrack(0)
    print_func()

 

運行結果:

 


免責聲明!

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



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