問題描述:
有n個集裝箱要裝上2艘載重量分別為c1和c2的輪船,其中集裝箱i的重量為wi,且∑wi <= c1 + c2。
問是否有一個合理的裝載方案,可將這n個集裝箱裝上這2艘輪船。如果有,找出一種裝載方案。
問題分析:
如果一個給定裝載問題有解,則采用下面的策略可得到最優裝載方案。
(1)首先將第一艘輪船盡可能裝滿;
(2)將剩余的集裝箱裝上第二艘輪船。
將第一艘輪船盡可能裝滿等價於選取全體集裝箱的一個子集,使該子集中集裝箱重量之和最接近c1。由此可知,裝載問題等價於以下特殊的0-1背包問題:
max∑ wi * xi && ∑ wi * xi <= c1, xi ∈ {0, 1}, 1 <= i <= n;
輸入樣例:
10 10 4 7 4 3 5
輸出樣例:
放入第一艘輪船的集裝箱: 1 3 放入第二艘輪船的集裝箱: 2 4 BestWeight = 10
代碼:
1.遞歸回溯
#include <bits/stdc++.h> using namespace std; int capacity1,capacity2; //輪船1,2的載重量 int n; //n個集裝箱 int weight[1000]={0}; // n個集裝箱的重量, weight【i】表示第i個集裝箱的重量 int PreWeight = 0; // 深度搜索時,當前載重 int RestWeight = 0; // 深度搜索時,剩余重量 ,注意是未搜索結點的重量 int BestWeight = 0; // 記錄最大載重,初始為一個很小的數 bool record[1000]={0}; // 記錄當前方案有哪些集裝箱被放進第一艘輪船 ,record【i】= 1 表示第i個集裝箱被放進去了 bool BestRecord[1000]={0}; int k=1; // 剪枝函數 PreWeight + weight[dep] <= capacity1 //上界函數 RestWeight + PreWeight > BestWeight void GetBestRecord() // 第一艘輪船最多載重的那個方案,有哪些集裝箱被放進第一艘輪船 (也叫構造最優解) { for(int i=1;i<=n;i++) { BestRecord[i]=record[i]; } return ; } void BackTrace(int dep) { if(dep>n) //搜索到葉子節點, { if( PreWeight > BestWeight ) { BestWeight = PreWeight; // 記錄最優解 GetBestRecord(); // 保存路徑 } return; } RestWeight = RestWeight - weight[dep]; // 想想為什么要放這里:搜索到該結點,不管它放不放進第一艘輪船,RestWeight都得減 // 第 dep 個集裝箱裝入第一艘船 if( PreWeight + weight[dep] <= capacity1 ) //能裝的下不 { record[dep] = 1; PreWeight = PreWeight + weight[dep]; if( RestWeight + PreWeight > BestWeight ) BackTrace(dep+1); record[dep] = 0; PreWeight = PreWeight - weight[dep]; RestWeight = RestWeight + weight[dep]; } // 第 dep 個集裝箱不裝入第一艘輪船 if( RestWeight + PreWeight > BestWeight ) BackTrace(dep+1); RestWeight = RestWeight + weight[dep] ; return; } int main() { // 輸入 cin>>capacity1>>capacity2; cin>>n; for(int i=1;i<=n;i++) { cin>>weight[i]; RestWeight = RestWeight + weight[i] ; //獲得總重量 } //搜索 BackTrace(1); //輸出 cout<<"第一艘輪船的集裝箱:"; for(int i=1;i<=n;i++) { if(BestRecord[i]) cout<<i<<" "; } cout<<endl; cout<<"第二艘輪船的集裝箱:"; for(int i=1;i<=n;i++) { if(!BestRecord[i]) cout<<i<<" "; } cout<<endl; cout<<"BestWeight="<<BestWeight<<endl; return 0; }