回溯法求解裝載問題(DFS 剪枝策略)

2021-10-12 05:11:16 字數 2039 閱讀 7200

參考:

問題描述:

有n個貨櫃要裝上2艘載重量分別為c1和c2的輪船,其中貨櫃i的重量為wi,且

問是否有乙個合理的裝載方案,可將這n個貨櫃裝上這2艘輪船。如果有,找出一種裝載方案。

問題分析:

如果乙個給定裝載問題有解,則採用下面的策略可得到最優裝載方案。

(1)首先將第一艘輪船盡可能裝滿;

(2)將剩餘的貨櫃裝上第二艘輪船。

將第一艘輪船盡可能裝滿等價於選取全體貨櫃的乙個子集,使該子集中貨櫃重量之和最接近c1。由此可知,裝載問題等價於以下特殊的0-1揹包問題:

演算法思路:

子集樹表示解空間,則解為n元向量, xi∈ 。

約束函式:

當前搜尋的層i <= n時,當前擴充套件結點z為子集樹的內部結點,對於當前擴充套件結點,其右子樹約束函式值與其父節點相同,則僅當滿足cw+w[i] <= c時進入左子樹,x[i]=1; 當cw+w[i] > c ,在以結點z為根的子樹中所有結點都不滿足約束條件,因而該子樹中解都是不可行解,因而將在該子樹刪去。

限界函式:

由於是最優化問題, 可利用最優解性質進一步剪去不含最優解的子樹:

設z是解空間樹第i層上的當前擴充套件結點。

設           bestw:  當前最優載重量, 

cw:  當前擴充套件結點z的載重量 ; 

r:  剩餘貨櫃的重量;

在以z為根的子樹中任意葉結點所相應的載重量不超過cw + r。因此,對於當前擴充套件結點,其左子樹限界函式值與其父節點相同,則僅當cw + r  ≤ bestw時,可將z的右子樹剪去。

即:cw +  r > bestw 時搜尋右子樹,x[i]=0;

#include #include using namespace std;

int n; //貨櫃數

int bw = 0; //best_weight:當前最優載重量(已搜尋的解空間樹中)

int cw = 0; //current_weight:當前載重量(從根結點到當前結點部分解)

int x[100]; //當前向量

int bestx[100]; //最優解向量

int w[100]; //weight:貨櫃重量

int c1; //第一艘船最大載重量

int c2; //第二艘船最大載重量

int r; //rest:剩餘貨櫃重量

void bacctrack(int i)

r -= w[i];

/* 剪枝 + 搜尋

右兒子的約束函式值與其父節點相同;

左兒子的限界函式值與其父節點相同;

所以在搜尋左兒子時,則只需判斷約束函式能否將其剪枝

搜尋右兒子時,則只需判斷限界函式能否將其剪枝

*/ //搜尋左兒子

if(cw + w[i] <= c1)

//搜尋右兒子

if(cw + r > bw) }

int main() }

if(rest_sum > c2)

cout << "無法裝入" << endl;

else

cout << endl;

cout << "第二艘船裝入的貨物是:";

for(int i = 1; i <= n; i++)

cout << endl;

} return 0;

}

測試樣例:(1)

回溯法求解裝載問題

有n個貨櫃要裝上一艘載重量為w的輪船,其中貨櫃i 1 i n 的重量為wi。不考慮貨櫃的體積限制,現要從這些貨櫃中選出重量和小於等於w並且盡可能大的若干裝上輪船。例如,n 5,w 10,w 時,其最佳裝載方案是 1,1,0,0,1 或者 0,0,1,1,0 maxw 10。採用帶剪枝的回溯法求解。問...

回溯法解裝載問題

遞迴解法 include using namespace std int bestw 0 int cw 0 int num 3 int r 46 int bestx 3 void load int w,int c,int n,int x r w n int main void int x load ...

回溯法,回溯法解裝載問題

利用回溯法解問題時一般按以下三步驟 1 定義問題的解空間 2 確定易於搜尋的解空間結構 3 以深度優先策略搜尋解空間,並在搜尋過程中用剪枝函式避免無效搜尋 二 回溯法應用 裝載問題 一批貨櫃共n個要裝上2艘載重量分別為c1和c2的輪船,其中貨櫃i的重量為wi且w1 w2 wn c1 c2 試確定乙個...