算法设计——装载问题(两轮船)(回溯)

问题

在这里插入图片描述
要求确定一个合理的装载方案将n个集装箱装入两艘船,如果有,找出一种方案

分析

我们之前用贪心做过一艘船的最优装载问题(将尽可能多的集装箱装入),即将重量小的集装箱先装。

那么我们看看这个题,如果用贪心的话能不能做出来。
假设两艘船载重量为100 100,
假设有5个物体,重量为20 30 40 50 60,如果我们用贪心算法,那么第一艘船装入的物品为 20 30 40,共三个
那么另一艘船只能放入50,共一个
此时我们发现贪心算法明显出了问题

因为可以第一艘船放入:20 30 50
第二艘船放入40 60
此时刚好完全装入

所以我们需要另一种方法:
将第一艘船装入尽可能重的集装箱
当剩余集装箱重量小于第二艘船时,那么能够装下所有集装箱。

如何使第一艘船装入尽可能多的集装箱呢?
这有点类似于0-1背包问题
但我们会使用回溯法,因为某些情况下回溯法优于动态规划

此时解空间为子集树
当所在状态的重量加上所在点重量小于载重量时(越界判断),我们就可以进入左子树进行搜索尝试。
当所在状态的重量加上所有剩余重量大于当前最优值时**(剪枝)**,我们就可以进入右子树进行判断。

代码实现

#include<stdio.h>
#define N 100
int c1,c2;//装载量 
int n; //物品数目 
int w[N];//物品重量 
int flag[N]={0};//标记
int r=0;//总余量 
int cw=0;//当前重量
int bw=0;//最优解
int choose[N];//保存数组 
void backtrack(int i){
	int j;
	if(i>n){//到达叶节点 
		if(cw>bw){//更新最优值 
			bw=cw;
			for(j=1;j<=n;j++){
				choose[j]=flag[j];
			}
		}
		return ;
	}
	r-=w[i];//更新剩余容量 
	if(cw+w[i]<=c1){//左子树(加上此节点重量不超过c1) 
		cw+=w[i];//更新现有重量 
		flag[i]=1;//标记,即记录数据 
		backtrack(i+1);//递归 
		cw-=w[i];//恢复 
		flag[i]=0;
	}
	if(cw+r>bw){//右子树,如果现有重量加上剩余量不超过最优值时剪枝 
		backtrack(i+1);//递归 
	} 
	r+=w[i];//恢复余量 
}
int main()
{
	int i,sum=0;
	printf("请输入c1,c2装载量和物品数目:"); 
	scanf("%d %d %d",&c1,&c2,&n);
	for(i=1;i<=n;i++){
		scanf("%d",&w[i]);
		r+=w[i]; 
		sum+=w[i]; 
	}
	backtrack(1);
	if(sum-bw<=c2){
		printf("能够将物品装下\nc1放的物品为:");
		for(i=1;i<=n;i++){
			if(choose[i]==1) printf("%d ",i);
		} 
		printf("\nc2放的物品为:");
		for(i=1;i<=n;i++){
			if(choose[i]==0) printf("%d ",i);
		} 
	} else{
		printf("不能放入所有物品\n"); 
	}
} 
  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值