1、简单装载问题

[问题描述]
有n个集装箱要装上一艘载重量为W的轮船,其中集装箱i ( 1≤i≤n )的重量为wi。不考虑集装箱的体积
限制,现要这些集装箱中选出若干装上轮船, 使它们的重量之和等于W ,当总重量相同时要求选取的集装箱个数尽可能少。

【求解算法】:
                      void dfs(int num,int tw,int rw,int op[ ],int i);
                      num :选择的集装箱个数;
                      tw : 选择的集装箱重量和
                      rw : 剩余集装箱的重量和; 
                      op : 表示一个解,即一个选择方案,选择时,op=1,不选时,op=0
                       i : 表示考虑的集装箱i

对于第i层的某个分枝结点,对应的调用状态是dfs (num, tw, rw, op,i) ,对应的扩展结点有2个:
左分支:
          ●选择第i个
          ➢op[i]=1 , num=num+1, tw=tw+w[i], rw=rw-w[i],转到下一个状态dfs(num, tw, rw,op,i+1) 
右分支:
          ●不选择第i个,
          ➢op[i]=0, num不变, tw不变, rw=rw-w[i] ,转到下一 个状态dfs (num, tw, rw, op,i+1),这个状               态和上一个状态的区别是,该状态中的num,tw没有变
每一次扩展不需要作出所有的扩展,只需要剪枝即可 

【剪枝方式】
                   ●左剪枝:
                   ➢tw+w[i]<=w则扩展,否则不扩展(不超过载重量就扩展)
                   ●右剪枝
                   ➢tw+rw-w[i]>=w则扩展,否则不扩展
                   相当于➢tw+rw-w[i]>=max则扩展,否则不扩展

【剪枝过程】

初始,num=0,tw=0,rw=所有集装箱的重量和20,括号里我们用来表示选与不选的向量表示(选择时用1表示不选 用0表示),i=1,首先进行左分支,若【选择】第一个集装箱,那么,tw=第一个集装箱的重量5,rw=20-5=15,判断当前是否满足tw+w[i]<=w,5<10满足,则向量里标记为1,num=0+1=1,继续判断下一个左分支(注:每次都默认先拓展左分支,左分支被剪掉再判断右分支,右分支也被剪掉再回溯),i=2,若【选择】第二个集装箱,那么 tw=tw+w[i]=5+2=7,rw=15-2=13,判断tw+w[i]=7<10,向量标记为1,num=2,继续扩展左分支,i=3,若【选择】第三个集装箱, tw=tw+w[i]=7+6=13>10超过了载重,所以剪掉该分支,拓展其右分支,即【不选】第三个集装箱,向量表示为0,tw=7,rw=rw-w[i]=13-6=7, 判断tw+rw-w[i]=7+13-6=14>10,则需要扩展,先扩展左分支,i=4,tw=7+4=11>10,所以不选择,左分支被剪掉,向量用0表示,再拓展右分支,此时是【不选】第4个集装箱,判断tw+rw-w[i]=7+7-4=10=w,所以可以扩展,向量用0表示,继续拓展左分支,i=5,若【选择】第五个集装箱,tw=7+3=10=w, 满足条件,rw=3-3=0, 所以num=3, 向量标记为1,此时暂时认为这个解就是最优解,将其保存,然后再看右分支,tw+rw-w[i]=7+3-3=7<w,所以不拓展,右分支被剪掉,此时所有集装箱都判断完,则回溯,一直回溯到第二个集装箱,(因为i=2没有判断右分支),然后判断tw+rw-w[i]=5+15-2=18>w, 所以拓展,然后一直判断下去直到i=5,然后再回溯到i=1的右分支,继续重复,得到第二个最优解(0,0,1,1,0),此时它的num=2 < 第一个num=3,所以它是最优解

【详细图解参考】:

【代码】

#include <stdio.h>
#include <string.h>   //如果用到memset函数记得要加这个头文件!!
#define MAXN 20
//全局变量
int w[]={0,5,2,6,4,3} ;
int n=5,W=10;
int maxw=0;   //存放最优解的总重量,初值是0
int x[MAXN]; //存放最优解向量,初值是0
int minnum=999999;   //存放最优解的集装箱个数,初值为最大值void dfs(int num,int tw,int rw,int op[],int i){if(i>n){  //i>n说明所有的集装箱都判断完了 if(tw==W&&num<minnum){ //满足这个条件即为最优解,则覆盖之前的解 maxw=tw;minnum=num;for(int j=1;j<=n;j++){x[j]=op[j];  //把最优解的值依次赋给最优解向量 }}}else{  //没到最后一个集装箱则需要判断当前这个集装箱是选还是不选 op[i]=1;  //如果选择 if(tw+w[i]<=W){  //判断剪枝条件 dfs(num+1,tw+w[i],rw-w[i],op,i+1);}op[i]=0;  //如果不选if(tw+rw-w[i]>=W){  //判断剪枝条件 dfs(num,tw,rw-w[i],op,i+1);} }
}
void displaySolution(int n){for(int i=1;i<=n;i++){if(x[i]==1){printf("选择第%d个集装箱\n",i);}}printf("总重量为%d\n",maxw);
}
int main(){int op[MAXN];   //op存放所有可行解 int rw;memset(op,0,sizeof(op));  //初始化为0 for(int i=1;i<=n;i++){rw=rw+w[i];   //初始剩余量=所有集装箱饿和 }dfs(0,0,rw,op,1);  //从第一个开始displaySolution(n);  //输出解return 0;
} 
#include <string.h>   //如果用到memset函数记得要加这个头文件!! 

【算法分析】

2、复杂装载问题

【问题描述】

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

n=3,c1=c2=50;
w={0,10,40,40};

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

【装载方案】:
(1)首先将第一艘轮船尽可能装满  第一艘轮船的最优化(简单装载)问题
(2 )然后将剩余的集装箱装在第二艘轮船上。

【解表示】:
                    x[0....n]:  存放可行解
                    tw :  已经装载到第一艘轮船上的集装箱重量之和
                    rw :  剩余集装箱的重量和
                     i :     表示考虑的第i个集装箱
                    maxw:  当前装载方案中,最优方案中的最大装载的集装箱的重量总和
【求解步骤】:
( 1 )将尽可能多的集装箱装到第一 艘船上,得到解向量x
对于第i个集装箱的装载,扩展规则(与简单装载相同)是:
              ●如果tw+w[i]<=c1,说明第一艘船可以装下,就扩展左分枝, x[i]=1, tw=tw+w[i], rw=rw-w[i]
              ●如果tw+rw-w[i] >maxw,就扩展右分枝, x[i]=0, rw=rw-w[i]
( 2)累计第一艘装完后剩余的集装箱重量sum 
( 3 )若sum<=c2,表示第二艘轮船可以装完,返回true ;否则表示第二艘轮船不能装完,返回false.

【求解算法】:
                      void dfs(int tw,int rw,int op[ ],int i);
                      tw : 选择的集装箱重量和
                      rw : 剩余集装箱的重量和
                      op :表示一个解,即一个选择方案
                       i : 表示考虑的集装箱i

【剪枝过程】:

初始,tw=0,rw=10+40+40=90, op为初始状态向量(0,0,0,0)(第一个0不用), i=1,如果第一个集装箱选择,tw=0+10<c1,满足条件可以选择,则rw=90-10=80,op[1]=1, 向量为(0,1,0,0),i=2,如果第二个集装箱选择,tw=10+40=50刚好等于c1,也可以选择,则rw=80-40=40, op[2]=1, 向量为(0,1,1,0),i=3,因为已经等于第一艘船的全部重量了所以肯定不能再继续拓展左分支,开始拓展右分支,即不选择第三个集装箱,那么tw不发生变化为50,rw=rw-w[i]=40-40=0, 向量为(0,1,1,0), 到此我们得到第一个可行解(0,1,1,0),maxw=50,然后回溯到i=2时的右分支,即第二个集装箱不被选择时,tw不发生变化为10,rw=rw-w[i]=80-40=40,向量为(0,1,0,0), i=3,  然后判断这个节点的左分支要不要扩展, tw+w[i]=10+40=50<=c1, 所以可以扩展,到此又得到一个可行解(0,1,0,1) ,此时的最优解最大载重量没有超过上一个可行解,所以不替换,然后回溯到i=3的右分支,此时tw+rw-w[i]=10+40-40=10<maxw, 所以右分支剪去,再回溯到I=1的右分支,即不选择第一个箱子tw=0,rw=rw-w[i]=90-10=80,向量为(0,0,0,0),考虑该节点是否扩展左分支,因为tw+w[i]=0+40=40<c1,所以可以扩展,扩展后,tw=0+40=40,rw=80-40=40,向量为(0,1,0,1),再看是否扩展下一个左分支,此时tw+w[i]=40+40=80>c1,所以剪枝,再看右分支,不被选择时,tw=40,rw=rw-w[i]=40-40=0, tw+rw-w[i]=40+0-40=0<maxw, 所以剪枝,回溯到上一个右分支,tw=0,rw=80-40=40,tw+rw-w[i]=40<maxw, 还是剪枝

【图解参考】

得到最优解后,计算sum

代码

#include <stdio.h>
#include <string.h>
#define MAXN 20
//全局变量
int w[]={0,10,40,40} ;  //不用下标0的元素
int n=3,c1=50,c2=50;
int maxw=0;   //存放第一艘船最优解的总重量,初值是0
int x[MAXN]; //存放第一艘船最优解向量,初值是0void dfs(int tw,int rw,int op[],int i){if(i>n){  //i>n说明所有的集装箱都判断完了 if(tw<=c1&&tw>maxw){ //满足这个条件即为最优解,则覆盖之前的解 maxw=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]>maxw){  //判断剪枝条件 dfs(tw,rw-w[i],op,i+1);} }
}
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 displaySolution(int n){for(int i=1;i<=n;i++){if(x[i]==1){printf("将第%d个集装箱装上第一艘船\n",i);}else{printf("将第%d个集装箱装上第二艘船\n",i);}}
}
int main(){int op[MAXN];memset(op,0,sizeof(op));int rw;for(int i=1;i<=n;i++){rw+=w[i];}dfs(0,rw,op,1);  ///求第一艘船的最优解 if(solve()){printf("最优方案为:\n");displaySolution(n);}else{printf("没有合适的方案");}
}

【回溯法】简单装载问题与复杂装载问题相关推荐

  1. 回溯法简单应用--解数独

    简单介绍 数独是当下较为流行的数学游戏之一.通常数独由9x9的格子构成,其中将9x9的格子分为9个3x3的区域,称为"宫"(通常宫与宫之间会用较粗的线来分隔).游戏的目标则是在格子 ...

  2. 回溯法 | 求解装载问题

    问题描述: 有 n 个集装箱要装上一艘载重量为 W 的轮船,其中集装箱 i (1<=i<=n) 的重量,为wi.子啊装在体积不受限制的情况下,将尽可能重的集装箱装上轮船,当重量相同时要求选 ...

  3. 0027算法笔记——【回溯法】回溯法与装载问题

    1.回溯法 (1)描述:回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法.  (2)原 ...

  4. 算法设计与分析——回溯法——装载问题

    0027算法笔记--[回溯法]回溯法与装载问题 自己写的代码: #include <iostream> using namespace std; template <class Ty ...

  5. 回溯法,回溯法解装载问题

    一.回溯法有"通用的解题法"之称,可以系统的搜索一个问题的所有解或任一解.它在问题的解空间中按深度优先策略,从根节点出发,搜索解空间树.算法搜索至解空间树的任一节点时,先判断该节点 ...

  6. 使用回溯法求解装载问题

    回溯法解决装载问题(java实现) 问题描述:有n个集装箱要装上两艘载重量分别为C1和C2的轮船,其中集装箱i的重量为wi,且:∑ wi ≤ C1+C2. 求是否有一个合理的装载方案能将这 n 个集装 ...

  7. 集装箱装载问题-回溯法解决

    1.算法设计 一.回溯法是一个搜索了所有的解空间的算法,在搜索的过程中通过具体问题条件不断选优判断,最后达到一个合适的目标或者全部搜索仍无想要的答案.它是通过分步的方法进行搜索,当当前这一步无法达到目 ...

  8. 用c语言验证装载问题 回溯法,《算法分析与设计》期末考试复习题纲(完整版)...

    <算法分析与设计>期末复习题 一.选择题 1. 算法必须具备输入.输出和( D )等4个特性. A.可行性和安全 性 B .确定性和易读性 C.有穷性和安全 性 D .有穷性和确定性 2. ...

  9. android迷宫生成算法,【Unity算法实现】简单回溯法随机生成 Tile Based 迷宫

    算法学习自 作者eastecho 在IndieNova上发表的文章 简单的使用回溯法生成 Tile Based 迷宫 , 我只是简单做了一下Unity的实现. 基础算法的简单说明 简单说明一下算法步骤 ...

  10. 几个常见的简单的算法(暴力法,递推法,枚举法,递归法,分治法,贪心法,回溯法)

    最近在学习算法相关知识. 通过买的视频教程了解到了一些简单的算法,为了加深感悟,同时也为了理解,将这几个常见的算法的定义进行记录. 算法是程序的灵魂,也可以认为是程序最重要的部分. 在通过算法解决问题 ...

最新文章

  1. APPLE框架之高效便捷的Repository解决方案
  2. 递归求二叉树的深度_优雅地用堆栈替代递归实现二叉树的深度优先搜索
  3. 20080331 - What is a PID, How is it useful when troubleshooting a system
  4. 阿里程序员转行公务员,工资少了40万,只留一句话惊醒众人
  5. Git如何从众多提交中保留个别提交
  6. Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
  7. centos安装mysql wsl_windows 10 WSL 安装 Centos
  8. 【学习笔记】Unreal(虚幻)4引擎入门(三)
  9. DPCM差分预测编码原理及实现
  10. ps色轮插件Coolorus v2.5.14(专业的Photoshop配色插件,支持PS CC 2019)
  11. 问题解决:你需要trustedinstaller提供的权限才能删除
  12. uniapp项目 App端实现微信登录、QQ登录
  13. 有关Windows10中诊断和反馈隐私设置
  14. html中加粗的字体如何改细,css font-weight 属性设置文本字体的粗细
  15. Java设计模式之——适配器
  16. 嵌入式Linux开发常用命令总结
  17. Vue3.0----综合案例(第七章)
  18. Unity(一)入门:Unity Hub下载 Unity安装
  19. 骆驼命名法,帕斯卡命名法与下划线命名法
  20. Linux服务器搭建----Web服务器(apache)

热门文章

  1. 复活天若OCR的谷歌翻译接口
  2. 1_Java语言概述
  3. 三星s8 升级android9,三星S8官方国行版安卓9固件系统线刷升级包:CHC-G9500ZCS3DSF5...
  4. 2.查询平均成绩大于60分的同学的学号和平均成绩
  5. 基于 ETC 专用短程通信的车路协同 第 1 部分:应用集及应用数据交互需求
  6. 基于javaweb的crm客户关系管理系统(java+springboot+echarts+freemarker+layui+mysql)
  7. 浅谈DM达梦数据库安全管理之用户身份验证与权限管理
  8. iptables详解(三)
  9. MOAC区块链助记词
  10. 计算机专业专科学校排名及分数线,2017广东专科学校排名及分数线