装载问题
问题描述
- 有一批共n个集装箱要装上2艘载重量分别为C1和C2的轮船,其中集装箱i的重量为wi,且∑wi≤C1+C2,装载问题要求确定一个合理的装载方案可将这n个集装箱装上这2艘轮船。
- 容易证明,如果一个给定装载问题有解,则采用下面的策略可得到最优装载方案:
(1) 首先将第一艘轮船尽可能装满;
(2) 将剩余的集装箱装上第二艘轮船。
证明过程:
假设wt是已经装上第一艘轮船的集装箱重量和,由于题目要求,则需所有集装箱的重量减去wt得小于或等于第二艘轮船的最大载重量,又由于第二艘轮船的最大载重量和总集装箱重量为固定值,则wt需尽可能地大才能满足该要求。
定义解空间
将第一艘轮船尽可能装满等价于选取全体集装箱的一个子集,使该子集中集装箱重量之和最接近C1,该子集就是装载问题的解空间
确定解空间结构
由上可见,装载问题是特殊的01背包问题(不用考虑物品价值、仅考虑重量)
因此解空间结构同样是子集树
剪枝函数
代码实现
public class clproblem {
static int n;//集装箱数
static int[] w;//集装箱重量数组
static int c;//第一艘轮船的载重量
static int cw;//当前载重量
static int bw;//当前最优载重量
static int r;//岸上可供选择的集装箱总重量
static int[]x;//当前解
static int[]bx;//当前最优解
public static void init(int num,int[] ww,int cc) {
//初始化类数据成员
n=num;
w=ww;
c=cc;
cw=0;
bw=0;
x=new int[n+1];
bx=x;
for(int i=0;i<n;i++)
r+=w[i];//初始化
backtrack(1);
}
public static void backtrack(int t) {
//搜索第i层结点
if(t>n) {//到达叶结点
for(int j=1;j<=n;j++)
bx[j]=x[j];
bw=cw;
return;
}
//搜索子树
r-=w[t-1];//岸上剩余可选择集装箱总重量
if(cw+w[t-1]<=c) {
//搜索左子树
x[t-1]=1;
cw+=w[t-1];
backtrack(t+1);//向下搜索
cw-=w[t-1];//回到当前可扩展结点需要减掉往下时加的重量
}
if(cw+r>bw) {
//搜索右子树,右子树都是不放入,因此默认右子树都可达
x[t-1]=0;
backtrack(t+1);
}
r+=w[t-1];//返回当前可扩展结点需要加上可选择集装箱重量
}
public static void main(String[] args) {
System.out.println("请输入集装箱个数:");
Scanner sc=new Scanner(System.in);
int num=sc.nextInt();
int[]ww=new int[num];
System.out.println("请输入各个集装箱的重量:");
for(int i=0;i<num;i++)
ww[i]=sc.nextInt();
System.out.println("请输入第一艘轮船的最大承载重量:");
int cc=sc.nextInt();
init(num,ww,cc);
System.out.print("此时最优解为:\n{");
for(int i=0;i<num;i++) {
if(i==num-1)
System.out.println(bx[i]+"}");
else
System.out.print(bx[i]+",");
}
System.out.println("最优载重量为:\n"+bw);
}
}