參考:
問題描述:
有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 試確定乙個...