【问题描述】
有n个集装箱要装上2艘载重量分别为c1和c2的轮船,其中集装箱i的重量为wi,且∑wi <= c1 + c2。问是否有一个合理的装载方案,可将这n个集装箱装上这2艘轮船。如果有,找出一种装载方案。
【输入形式】集装箱个数n,载重c1、c2,每个集装箱的重量wi
【输出形式】如果有方案,列出各船最优装载策略。或输出“NO solution”
【样例输入】
7 152 130
90 80 40 30 20 12 10
【样例输出】
1 3 6 7
2 4 5
【样例说明】
【评分标准】
1.装载策略
可以把集装箱尽可能重的装载到c1然后判断剩余重量是否大于c2,然后判断是否存在装载方案
2.变量说明
tw:已装载到c1的集装箱重量之和
rw:未装入轮船的集装箱重量
op:当前装载方案
x:最优装载方案
best:c1最多装载的质量
3.算法思路
将尽可能多的集装箱装到第一艘船上,得到解向量x,对于第i个集装箱的装载,扩展规则是: 对于第i个集装箱的装载,扩展规则是:
如果tw+w[i]<=c1,就扩展左分枝,X[i]=1,tw=tw+w[i],rw=rw-w[i]
如果tw+rw-w[ i]>best,就扩展右分枝,x[i]=0, rw=rw-w[i]
4.关键代码
void dfs(int tw,int rw,int op[],int i){//求第一艘轮船的最优解方案
if(i>n){
if(tw>best){
best=tw;
for(int j=1;j<=n;j++){ //找到满足条件的更优解,更新best并复制更优解方案
x[j]=op[j];
}
}
}else{
op[i]=1;//选择第一个集装箱
if(tw+w[i]<=c1){//左孩子剪枝
dfs(tw+w[i],rw-w[i],op,i+1);
}
op[i]=0;//不选择第一个集装箱,回溯
if(tw+rw-w[i]>best){//右孩子剪枝
dfs(tw,rw-w[i],op,i+1);
}
}
}
5.完整代码
#include <bits/stdc++.h>
using namespace std;
const int maxs=1e+6;
int n,c1,c2;
int w[maxs],x[maxs],op[maxs];
int tw,rw,i,best;
void dfs(int tw,int rw,int op[],int i){
if(i>n){
if(tw>best){
best=tw;
for(int j=1;j<=n;j++){
x[j]=op[j];
}
}
}else{
op[i]=1;
if(tw+w[i]<=c1){
dfs(tw+w[i],rw-w[i],op,i+1);
}
op[i]=0;
if(tw+rw-w[i]>=best){
dfs(tw,rw-w[i],op,i+1);
}
}
}
void print(int x[]){
int sum=0;
for(int i=1;i<=n;i++){
if(x[i]==0){
sum+=w[i];
}
}
if(sum>c2){
cout<<"NO solution"<<endl;
}else{
for(int i=1;i<=n;i++){
if(x[i]==1){
cout<<i<<" ";
}
}
cout<<endl;
for(int i=1;i<=n;i++){
if(x[i]==0){
cout<<i<<" ";
}
}
}
}
int main(){
cin>>n>>c1>>c2;
for(int i=1;i<=n;i++){
cin>>w[i];
rw+=w[i];
}
dfs(tw,rw,op,i);
print(x);
return 0;
}
6.问题总结
本题模型为一个二叉树,通过深度优先遍历来寻找最优解,这其中用到了回溯算法,递归算法
其中递归算法较为抽象,读者可以多看一下递归算法的视频了解其原理及过程。
7.视频链接
如果还是不懂 可以看同步的视频有助于理解
https://www.bilibili.com/video/BV1K64y1F7m9?spm_id_from=333.337.search-card.all.click