【資料結構與演算法】回溯法解決裝載問題
阿新 • • 發佈:2018-11-27
回溯法解決裝載問題(約束函式優化)
解題思想
遍歷各元素,若cw+w[t]<=c(即船可以裝下),則進入左子樹,w[t]標記為1,再進行遞迴,若cw+r>bestw(即當前節點的右子樹包含最優解的可能),則進入右子樹,否則,則不遍歷右子樹。
完整程式碼實現如下
public class Loading {
static int n;//貨箱數目
static int[] w;//貨箱重量陣列
static int c;//第一艘船的重量
static int cw;//當前裝載的重量
static int bestw;//目前最優裝載的重量
static int r; //剩餘貨箱的重量
static int[] x;//當前解,記錄從根至當前結點的路徑
static int[] bestx;//記錄當前最優解
public static int MaxLoading(int[] ww,int cc) {
//初始化資料成員,陣列下標從1開始
n = ww.length - 1;
w = ww;
c = cc;
cw = 0;
bestw = 0;
x = new int[n+1];
bestx = new int[n+1];
//初始化r,即剩餘最大重量
for(int i =1;i<=n;i++) {
r += w[i];
}
//計算最優載重量
backtrack(1);
return bestw;
}
//核心演算法
public static void backtrack(int t) {
//到達葉結點
if(t>n) {
if(cw>bestw) {
for(int i=1;i<=n;i++) {
bestx[i] = x[i];
}
bestw = cw;
}
return;
}
r -= w[t];
if(cw + w[t] <= c) {//搜尋左子樹
x[ t] = 1;
cw += w[t];
backtrack(t+1);
cw -= w[t];//回溯
}
if(cw + r>bestw) {
x[t] = 0;//搜尋右子樹
backtrack(t+1);
}
r += w[t];//恢復現場
}
/*
* 如果當前節點的右子樹不可能包含比當前最優解更好的解時,就不移動到右子樹上!
設bestw為當前最優解,Z為解空間樹的第i 層的一個節點
為剩餘貨箱的重量;當cw+r<=bestw時,沒有必要去搜索Z 的右子樹:
當前載重量cw+剩餘集裝箱的重量r>當前最優載重量bestw
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] ww = {0,20,30,60,40,40};
int c1 = 100;
int c2 = 100;
int n = ww.length - 1;
MaxLoading(ww,c1);
int weight2 = 0;//儲存第二艘船可能要裝的重量
for(int i=1;i<=n;i++) {
weight2 += ww[i]*(1-bestx[i]);//bestx[i]的值只能為0或1
}
if(weight2>c2) {
System.out.println("無法載滿貨物");
}
else {
System.out.println("第一艘船裝載貨物的重量: " + bestw);
System.out.println("第二艘船裝載貨物的重量: " + weight2);
//第一艘船的裝載情況
for(int i = 1;i<=n;i++) {
if(bestx[i] == 1) {
System.out.println("第" + i + "件貨物裝入第一艘船");
}
}
//第二艘船的裝載情況
for(int i = 1;i<=n;i++) {
if(bestx[i] == 0) {
System.out.println("第" + i + "件貨物裝入第二艘船");
}
}
}
}
}