参考:https://blog.csdn.net/m0_38015368/article/details/80196634
问题描述:
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且
问是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船。如果有,找出一种装载方案。
问题分析:
如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船。
将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。由此可知,装载问题等价于以下特殊的0-1背包问题:
且
算法思路:
用子集树表示解空间,则解为n元向量{x1, ... ,xn }, xi∈{0, 1} 。
约束函数:
当前搜索的层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 <iostream>
#include <cstring>
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)
{
//搜索到叶子结点,更新最优解
if(i > n)
{
for(int i = 0; i < n; i++)
bestx[i] = x[i];
bw = cw;
}
r -= w[i];
/*
剪枝 + 搜索
右儿子的约束函数值与其父节点相同;
左儿子的限界函数值与其父节点相同;
所以在搜索左儿子时,则只需判断约束函数能否将其剪枝
搜索右儿子时,则只需判断限界函数能否将其剪枝
*/
//搜索左儿子
if(cw + w[i] <= c1)
{
x[i] = 1;
cw += w[i];
bacctrack(i + 1);
cw -= w[i]; //回溯到父节点时,cw要更新为: cw - w[i]
}
//搜索右儿子
if(cw + r > bw)
{
x[i] = 0;
bacctrack(i + 1); //回溯到父节点时,r要更新为:r + w[i]
r += w[i];
}
}
int main()
{
cout << "请输入集装箱数量:" << endl;
cin >> n;
cout << "请输入两艘船最大载重量:" << endl;
cin >> c1 >> c2;
cout << "请输入集装箱重量:" << endl;
for(int i = 1; i <= n; i++)
cin >> w[i];
//将r初始化为所有集装箱的重量之和,那么当前扩展结点的r = r - w[i],避免每次计算的前结点的cw + r
for(int i = 1; i <= n; i++)
r += w[i];
bacctrack(1);
//判断在第一艘船尽可能装满后,剩下的集装箱能否装入第二搜船
int rest_sum = 0;
for(int i = 1; i <= n; i++)
{
if(bestx[i] == 0)
{
rest_sum += w[i];
}
}
if(rest_sum > c2)
cout << "无法装入" << endl;
else
{
cout << "第一艘船装入的货物是:";
for(int i = 1; i <= n; i++)
{
if(bestx[i] == 1)
cout << i << " ";
}
cout << endl;
cout << "第二艘船装入的货物是:";
for(int i = 1; i <= n; i++)
{
if(bestx[i] == 0)
cout << i << " ";
}
cout << endl;
}
return 0;
}
测试样例:
(1)
(2)