问题描述:
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且∑wi <= c1 + c2。
问是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船。如果有,找出一种装载方案。
问题分析:
如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案。
(1)首先将第一艘轮船尽可能装满;
(2)将剩余的集装箱装上第二艘轮船。
将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近c1。由此可知,装载问题等价于以下特殊的0-1背包问题:
∑ wi * xi <= c1, xi ∈ {0, 1}, 1 <= i <= n;
算法思路:
用排序树表示解空间,则解为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 <bits/stdc++.h>
using namespace std;
int n; //集装箱数
int cw; // 当前载重量, current weight
int bestw; //最优载重重量
int r; //剩余集装箱重量
int c1; //第一艘轮船的载重量
int c2; //第二艘轮船的载重量
int x[100]; //当前解
int bestx[100]; //当前最优解
int w[100]; //集装箱重量数组
void OutPut()
{
int restweight = 0;
for(int i = 1; i <= n; ++i)
if(bestx[i] == 0)
restweight += w[i];
if(restweight > c2)
printf("不能装入\n");
else
{
printf("船1装入的货物为:");
for(int i = 1; i <= n; ++i)
if(bestx[i] == 1)
printf(" %d", i);
printf("\n船2装入的货物为:");
for(int i = 1; i <= n; ++i)
if(bestx[i] != 1)
printf(" %d", i);
}
}
void BackTrack(int i)
{
if(i > n)
{
if(cw > bestw)
{
for(int i = 1; i <= n; ++i)
bestx[i] = x[i];
bestw = cw;
}
return;
}
r -= w[i];
if(cw + w[i] <= c1) //约束条件
{
cw += w[i];
x[i] = 1;
BackTrack(i + 1);
x[i] = 0;
cw -= w[i];
}
if(cw + r > bestw) //限界函数
{
x[i] = 0;
BackTrack(i + 1);
}
r += w[i];
}
void Initialize()
{
bestw = 0;
r = 0;
cw = 0;
for(int i = 1; i <= n; ++i)
r += w[i];
}
void InPut()
{
scanf("%d", &n);
scanf("%d %d", &c1, &c2);
for(int i = 1; i <= n; ++i)
scanf("%d", &w[i]);
}
int main()
{
InPut();
Initialize();
BackTrack(1);
OutPut();
}
测试样例:
测试样例一:
输入:
3
50 50
10 40 40
输出
船1装入的货物为: 1 2
船2装入的货物为: 3
截图:
测试样例2:
输入:
3
50 50
20 40 40
输出:
不能装入
截图:
标签:载重量,int,cw,回溯,装载,集装箱,最优,bestw From: https://blog.51cto.com/u_16129621/6350074