写在前面:感谢飞哥提出的问题,让我认识到自己对算法课本的忽视。这可是基础呀,从现在开始补吧!!!文章内容不免有错误和不足之处,欢迎大家指出,共同学习。
1 相关知识点
1.1什么是回溯法
基本思想:把问题的解空间转化为图或者树的结构,然后使用深度优先搜索策略进行遍历,再遍历的过程中记录和寻找所有可行解或者最优解;
1.2什么是子集树
所给的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间称为子集树;如01背包;
因为要处理所有的情况,子集树的复杂度是O(2^n);
如果需要记录子集树的最优解的选择,复杂度为O(n2^n);具体分析看下面的问题;
2 装载问题引入
2.1问题描述
n个集装箱装到2艘载重量为C1和C2的轮船A和B上,其中集装箱的重量为wi,且all(wi)<=c1+c2;
问是否有合适的装载方案,将n个集装箱放到这两艘轮船上;有找出方案。
2.2最优策略
A和B是相互独立的,即如何在A或者B上,放置物品是互不干扰的。
但是假如先在A上放置货物,则因剩下的货物不同,会使B上装载的货物的情况发生变化。因此,为了保证B上装载的情况,满足A和B将所有的货物都装下,A上应该尽可能的多放物品。
2.3 代码实现
/*
* @Author: cy
* @Date: 2020-05-10 21:52:29
* @LastEditors: cy
* @LastEditTime: 2020-05-10 22:25:37
* @FilePath: FromBook装载问题.cpp
* @Tags:
*/
#include<cstdio>
using namespace std;
const int num=100;
int n,c1,c2,w[num];// n个集装箱,A,B货轮载重量分别为C1,C2,W[i],第i个集装箱的重量
int cw,bw,rw;//cw,当前集装箱货物重量;bw,最优载重重量,rw,剩余集装箱重量;
int x[num],bx[num];//x[],A货轮的当前结果;bx[],A货轮的最优结果;
void BackTrack(int i)
{
//处理完了前n个集装箱;
if(i>n){
if(cw>bw){//cw,目前A中装了cw重量的集装箱;
//更新最优解;
bw=cw;
for(int i=1;i<=n;i++) bx[i]=x[i];
}
return;
}
//rw表示处理完第i个之后(选或不选),还剩下rw-w[i]重量的集装箱未处理;
rw-=w[i];
if(cw+w[i]<=c1){//cw,第i个货箱之前的重量 + 第i个货箱小于A的最大重量C1;
cw+=w[i];//加上
x[i]=1;//标记i被选
BackTrack(i+1);
cw-=w[i];//减去重量
x[i]=0;//撤销标记;
}
//不选择第i个物品的话;
//if cw:表示[1:i)的数据 rw:表示(i,n]的数据 ,不包括第i个的数据
//如果不包括第i的数据的和(cw+rw) 大于 目前最优解bw,则可以递归下去;
if(cw+rw > bw){
x[i]=0;
BackTrack(i+1);
}
//处理完第i个物品当前的情况了;
//因为再上一层,有两种情况;
//1;选择第i物品;
//2:不选择第i个物品
//如果目前处理的是上一层第1种情况,那么我们就有必要加上这个w[i];
//否则会影响上一层处理第2种情况;
rw+=w[i];
return ;
}
int main()
{
//读入数据;
scanf("%d%d%d",&n,&c1,&c2);
for(int i=1;i<=n;i++)
{
scanf("%d",&w[i]);
rw+=w[i];//rw表示目前最优集装箱的剩余重量;
}
//递归回溯
BackTrack(1);
//bw表示A货轮装下的货物重量;剩余的重量 > B可以放下的最多,则不可;
if(rw-bw>c2){
printf("没有装载方案n");
}else{
printf("货轮A:n");
for(int i=1;i<=n;i++) if(bx[i]) printf("%d ",i);
printf("n货轮B:n");
for(int i=1;i<=n;i++) if(0==bx[i]) printf("%d ",i);
}
return 0;
}