1. 题目描述

有一批共n个集装箱要装上两艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,
且w1+w2++wn≤c1+c2。
装载问题要求确定是否有一个合理的装载方案可将这些集装箱装上这两艘轮船。如果有,找出一种装载方案

例如:
n=3,c1=c2=50,w={10,40,40}时,可以将集装箱1和2装到第一艘轮船上,而将集装箱3装到第二艘轮船上。
n=3,c1=c2=50,w={20,40,40},则无法将这3个集装箱都装上轮船。

2. 解题思路

首先将第一艘轮船尽可能装满,然后将剩余的集装箱装在第二艘轮船上
(1)将尽可能多的集装箱装到第一艘轮船上,得到解向量x。
(2)累计第一艘轮船装完后剩余的集装箱重量sum。
(3)若sum<=c2,表示第二艘轮船可以装完,返回true;否则表示第二艘轮船不能装完,返回false。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 20

int w[] = {0, 10, 40, 40}; // 集装箱i的重量
int n = 3;
int c1 = 50, c2 = 50; // 轮船可装载重量
int maxw = 0; // 存放第一艘轮船最优解的总重量
int x[MAXN]; // 存放第一艘轮船最优解向量

void dispasolution(int n){
for(int j = 1; j <= n; j++){
if(x[j] == 1)
cout << "将第" << j << "个集装箱装入第一艘轮船" << endl;
else
cout << "将第" << j << "个集装箱装入第二艘轮船" << endl;
}
}

bool solve(){
int sum = 0; // 累计第一艘轮船装完后剩余集装箱重量
for(int j = 1; j <= n; j++){
if(x[j] == 0)
sum += w[j];
if(sum <= c2) // 第二艘轮船可以装完
return true;
else
return false; // 第二艘轮船不可以装完
}
}

void dfs(int i, int tw, int rw, int op[]){ // 求解第一艘轮船的最优解
if(i > n){ // 找到一个叶子节点
if(tw > maxw){ // tw表示选择的集装箱的总重量和
maxw = tw; // 找到一个满足条件的更优解,保存它
for(int j = 1; j < n; j++) // 复制最优解
x[j] = op [j];
}
}else{
if(tw + w[i] <= c1){
op[i] = 1; // 选取第i个集装箱
dfs(i + 1, tw + w[i], rw - w[i], op);
}
/*
右减枝的条件是,tw + 除了当前w[i],累加w[i + 1],w[i + 2]...w[n]后还大于最优解总重量方可进入条件
反推,tw + 除了当前w[i],累加w[i + 1],w[i + 2]...w[n]后如果小于最优解总重量,就没必要在寻找下去了,因为怎么寻找也不会满足条件了
*/
if(tw + rw - w[i] > maxw){
op[i] = 0; // 不选取第i个集装箱
dfs(i + 1, tw, rw -w[i], op);
}
}

}

int main(){
int op[MAXN]; // 存放临时解
memset(op, 0, sizeof(op)); // 初始化数组op
int rw = 0; // 剩余集装箱的和
for(int i = 0; i < n; i++)
rw += w[i];
dfs(1, 0, rw, op);
cout << "求解结果" << endl;
if(solve()){
cout << "最优方案" << endl;
dispasolution(n);
}
return 0;
}