问题描述:
有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; }