装载问题 (回溯法)

【问题描述】

有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

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值