回溯法解决装载问题(Java实现)
已知有 n 个集装箱(重量分别为 w1,w2,…,wn)和 1 艘轮船,轮船的载重量为 c,要求在不超过轮船载重量的前提下,将尽可能多的集装箱装入轮船。其中,第 i 个集装箱的重量为 wi。
求解的问题可形式化描述为:
即求装入轮船的集装箱的最大数量。在从根结点出发搜索问题的解时,需要考虑两个约束条件:
1) 可行性约束条件:
2) 最优解约束条件:remain+cw>bestw,其中,remain 表示剩余集装箱的重量,cw 表示当前已装上的集装箱的重量,bestw 表示当前的最优装载量。
对于不满足约束条件的情况:
则剪去该分支。判定第 i 层的结点是否装入轮船的条件为:
当 cw(i)>c 时,表示该分支上的所有结点均不满足约束条件,应剪去该分支。若对于当前正在搜索的结点来说,沿着当前的结点继续向下扩展到叶子结点,就是将剩下的集装箱都装入轮船,总重量仍然不会大于当前的最优值 bestw,则没有必要对该结点的子树进行扩展,应剪去该分支结点。
若当前的扩展结点为第 i 个结点,则剩下的结点表示集装箱的重量为:
该结点代表集装箱的重量上界为 cw(i)+r。
若输入仍然是 c=50、W={18, 25, 25},则解空间树的搜索过程如下图所示:

图 6 装载问题的搜索过程
初始时、cw=0、bestw=0、r=68,然后考察第 1 个集装箱,此时有 cw=0、bestw=0、r=50,由于cw+w[1]<c,因此可选取第 1 个集装箱,在选取第 1 个集装箱后,进入解空间树的第 2 层,即开始考察是否将第 2 个集装箱装入轮船。
考察第 2 个集装箱,此时有 cw=18、bestw=0、r=25,由于 cw+w[2]<50,因此可选取第 2 个集装箱,在选取第 2 个集装箱后,就进入解空间树的第 3 层,即开始考察是否将第 3 个集装箱装入轮船。
考察第 3 个集装箱,此时有 cw=43、bestw=0、r=0,由于 cw+w[3]>50,因此第 3 个集装箱不能装入轮船。于是往右子树方向走下去,即不装入第 3 个集装箱,到达叶子结点,有 cw=43、bestw=43,找到一个解 {1,1,0}。
经过剪枝后的子集树如下图所示:

图 7 经过剪枝后的子集树
算法实现如下:
对于右孩子结点,即不能装入轮船时,也就是 cw+w[t]>c 的情况,令 x[t]=0,表示为右分支结点,不装入轮船,然后继续处理下一层,即递归调用自身。代码如下:
问题分析
为了方便描述,用解向量 X=(x1,x2,…,xn) 表示哪一个集装箱装入轮船,其中,xi∈{0, 1},1≤i≤n,xi 的取值只有 0 或 1 两种情况,xi=1 表示第 i 个集装箱装入轮船,xi=0 表示第 i 个集装箱不装入轮船。求解的问题可形式化描述为:

即求装入轮船的集装箱的最大数量。在从根结点出发搜索问题的解时,需要考虑两个约束条件:
1) 可行性约束条件:

2) 最优解约束条件:remain+cw>bestw,其中,remain 表示剩余集装箱的重量,cw 表示当前已装上的集装箱的重量,bestw 表示当前的最优装载量。
对于不满足约束条件的情况:

则剪去该分支。判定第 i 层的结点是否装入轮船的条件为:

当 cw(i)>c 时,表示该分支上的所有结点均不满足约束条件,应剪去该分支。若对于当前正在搜索的结点来说,沿着当前的结点继续向下扩展到叶子结点,就是将剩下的集装箱都装入轮船,总重量仍然不会大于当前的最优值 bestw,则没有必要对该结点的子树进行扩展,应剪去该分支结点。
若当前的扩展结点为第 i 个结点,则剩下的结点表示集装箱的重量为:

该结点代表集装箱的重量上界为 cw(i)+r。
若输入仍然是 c=50、W={18, 25, 25},则解空间树的搜索过程如下图所示:

图 6 装载问题的搜索过程
初始时、cw=0、bestw=0、r=68,然后考察第 1 个集装箱,此时有 cw=0、bestw=0、r=50,由于cw+w[1]<c,因此可选取第 1 个集装箱,在选取第 1 个集装箱后,进入解空间树的第 2 层,即开始考察是否将第 2 个集装箱装入轮船。
考察第 2 个集装箱,此时有 cw=18、bestw=0、r=25,由于 cw+w[2]<50,因此可选取第 2 个集装箱,在选取第 2 个集装箱后,就进入解空间树的第 3 层,即开始考察是否将第 3 个集装箱装入轮船。
考察第 3 个集装箱,此时有 cw=43、bestw=0、r=0,由于 cw+w[3]>50,因此第 3 个集装箱不能装入轮船。于是往右子树方向走下去,即不装入第 3 个集装箱,到达叶子结点,有 cw=43、bestw=43,找到一个解 {1,1,0}。
经过剪枝后的子集树如下图所示:

图 7 经过剪枝后的子集树
算法实现如下:
import java.util.Scanner;
class BestNode {
int bestw;
int[] bestx;
final int NUM = 20;
BestNode() {
bestw = 0;
bestx = new int[NUM];
}
int getBestW() {
return bestw;
}
void setBestW(int cw) {
bestw = cw;
}
void setBestX(int[] x) {
for (int i = 0; i < x.length; i++) {
bestx[i] = x[i];
}
}
int getBestX(int i) {
return bestx[i];
}
}
public class ContainerLoading {
void Backtrack(int t, int n, int[] w, int c, int r, int cw, BestNode bNode, int[] x) {
if (t >= n) { // 达到叶子结点
if (cw > bNode.getBestW()) { // 若存在更优的解,则更新最优解
bNode.setBestW(cw);
for (int i = 0; i < x.length; i++) {
bNode.bestx[i] = x[i];
}
}
return;
}
if (cw + w[t] <= c) { // 尝试放入当前集装箱
x[t] = 1;
cw += w[t];
Backtrack(t + 1, n, w, c, r - w[t], cw, bNode, x);
cw -= w[t]; // 回溯,恢复状态
x[t] = 0;
}
Backtrack(t + 1, n, w, c, r, cw, bNode, x); // 不放入当前集装箱,继续尝试下一个
}
public static void main(String[] args) {
final int NUM = 100;
BestNode bNode = new BestNode();
ContainerLoading CL = new ContainerLoading ();
int c, n, r, cw;
int[] w;
int[] x;
Scanner sc = new Scanner(System.in);
System.out.print("请输入轮船的装载容量: ");
c = sc.nextInt();
System.out.print("请输入集装箱的数量: ");
n = sc.nextInt();
w = new int[n];
x = new int[n];
System.out.print("请分别输入" + n + "个集装箱的重量: ");
for (int i = 0; i < n; i++) {
w[i] = sc.nextInt();
}
cw = 0;
r = 0;
for (int weight : w) {
r += weight;
}
CL.Backtrack(0, n, w, c, r, cw, bNode, x);
System.out.println("最优装载量: " + bNode.getBestW());
System.out.print("装入轮船的集装箱编号: ");
for (int i = 0; i < n; i++) {
if (bNode.getBestX(i) == 1) {
System.out.print(" " + (i + 1));
}
}
System.out.println();
}
}
程序运行结果如下:
请输入轮船的装载容量:65 请输入集装箱的数量:3 请分别输入3个集装箱的重量:42 10 21 最优装载量:63 装入轮船的集装箱编号:1 3未到达叶子结点时,对该结点的两个分支分别进行处理。对于左孩子结点,若能装入轮船,也就是 cw+w[t]≤c 的情况,则令 x[t]=1、cw+=w[t],表示为左分支结点,装入轮船,然后继续处理下一层,即递归调用自身。代码如下:
if (cw + w[t] <= c) { // 搜索图片文字内容搜索左子树
x[t] = 1;
cw += w[t];
Backtrack(t + 1, n, w, c, r, cw, bNode, x);
cw -= w[t]; // 恢复状态
}
对于右孩子结点,即不能装入轮船时,也就是 cw+w[t]>c 的情况,令 x[t]=0,表示为右分支结点,不装入轮船,然后继续处理下一层,即递归调用自身。代码如下:
if (cw + r > bNode.getBestW()) { // 搜索按钮文搜索右子树
x[t] = 0;
Backtrack(t + 1, n, w, c, r, cw, bNode, x);
}
ICP备案:
公安联网备案: