分支限界法(Brach-and-Bound)

分支限界法与回溯法类似,也是在问题的解空间树上搜索问题的解,通过限界函数进行剪枝,但采用BFS广度优先策略搜索。

4.1基本思想

首先确定一个合理的限界函数,并根据限界函数确定目标函数的界[down,up];然后,按照广度优先策略搜索问题的解空间树:

1.在当前扩展结点处,生成所有儿子结点,估算所有儿子结点对目标函数的可能取值,舍弃不可能通向最优解的结点 (剪枝),将其余的加入到活结点表(用队列组织)中。

2.在当前活结点表中,依据先进先出或某种优先级(最小耗费或最大效益)策略,从当前活结点表中选择一个结点作为扩展结点。

3.重复(1)-(2)步骤,直到找到所需的解或活结点表为空。

分支限界法回溯法的区别

1.求解目标不同

回溯法的求解目标一般是找出满足约束条件的所有解或最优解

分支限界法的求解目标是找出满足约束条件的一个解或最优解

2.搜索方式不同

回溯法以深度优先的方式搜索解空间树

分支限界法以广度优先或以最小耗费(最大效益)优先的方式搜索解空间树

3.空间复杂度不同

这里介绍两种从活结点表选择下一个扩展结点的方法:

1.队列式分支限界法:按照队列先进先出原则选取下一个结点为扩展结点

2.优先队列式分支限界法:以最小耗费(最大效益)优先的方式搜索解空间树,即按照优先队列中规定的优先级选取优先级最高的结点称为当前扩展结点。常用堆(大根堆/小根堆)来实现。

4.2具体问题

以0/1背包问题为例具体来讲解分支限界法

4.2.1 0/1背包问题

问题描述:有n个重量分别为{w1,w2, … ,wn} 的物品,它们的价值分别为{v1,v2, … ,vn},给定一个容量为C的背包。 设计从这些物品中选取一部分物品放入该背包的方案,每个物品要么选中要么不选中,要求选中的物品重量和不超过C,且具有最大的价值。

确定剪枝函数(限界函数)

与回溯法类似

若为队列式分支限界法,则结点声明如下:

struct NodeType { //队列中的结点类型int no; //结点编号,从1开始int t; //当前结点在搜索空间中的层次int w; //当前结点的总重量int v; //当前结点的总价值int x[MAXN]; //当前结点包含的解向量double leftV; //剩余物品价值上界
};

若为优先队列式分支限界法,则结点声明如下:

struct NodeType { //队列中的结点类型int no; //结点编号int t; //当前结点在搜索空间中的层次int w; //当前结点的总重量int v; //当前结点的总价值int x[MAXN]; //当前结点包含的解向量double ub; //上界bool operator<(const NodeType &s) const { //重载<关系函数return ub<s.ub; //ub越大越优先出队}
};

确定解向量

我们知道分支限界法在搜索解空间树时,对于结点的处理是跳跃式的,回溯也不是单纯地沿着双亲结点一层一层地向上回溯,因此,当搜索到某个叶子结点且该对应一个可行解时,如何保存对应的解向量呢?

有两种可行办法:

1.每个结点带有一个可能的解向量。这种做法比较浪费空间,但实现起来简单。

2.每个结点带有一个双亲结点指针,当找到最优解时,通过双亲指针找到对应的最优解向量。这种做法需保存搜索经过的树结构,每个结点增加一个指向双亲结点的指针。

分支限界法求解的三个关键问题如下:

1.确定合适的限界函数,以及函数的界[down,up]。

2.如何组织待处理的活结点表。

3.如何构造解向量。

具体实现代码:

#include<stdio.h>
#include<queue>
#define MAXN 51
#define C 30
using namespace std;
int w[MAXN]= {0,16,15,15}; //背包的重量
int v[MAXN]= {0,45,25,25}; //背包的价值
int bestx[MAXN]; //最优解
int n=3; //背包个数
int bestv;
struct NodeType { //队列中的结点类型int t; //当前结点在搜索空间中的层次int w; //当前结点的总重量int v; //当前结点的总价值int x[MAXN]; //当前结点包含的解向量
};void bfs() {NodeType e,e1;int t;queue<NodeType>qu;e1.t=0;e1.no=1;e1.v=0;e1.w=0;e1.leftV=C;for(int i=1; i<=n; i++)e1.x[i]=0;qu.push(e1);while(!qu.empty()) {e=qu.front();qu.pop();t=e.t;if(t==n) {if(e.v>bestv) {bestv=e.v;for(int i=1; i<=n; i++) {bestx[i]=e.x[i];}}} else {e1.t=e.t+1;   for(int i=1; i<=n; i++)e1.x[i]=e.x[i];e1.w=e.w+w[e1.t];e1.v=e.v+v[e1.t];e1.x[e1.t]=1;if(e1.w<=30)qu.push(e1);e1.w=e.w;e1.v=e.v;e1.x[e1.t]=0;qu.push(e1);}}
}int main() {bfs();for(int i=1; i<=3; i++) {if(bestx[i]==1)printf("选择%d号背包\n",i);}printf("总价值为:%d\n",bestv);
}

4.2.2单源最短路径

采用队列式分支限界法求解

定义顶点结构:

struct NodeType //队列结点类型
{  int vno; //顶点编号int length; //当前路径长度
};

模拟这个过程:

代码如下:

void bfs(int v) { //求解算法NodeType e,e1;queue<NodeType> qu;e.vno=v; //建立源点结点e(根结点)e.length=0;qu.push(e); //源点结点e进队dist[v]=0;while(!qu.empty()) { //队列不空循环e=qu.front();qu.pop();//出队列结点efor (int j=0; j<n; j++) {if(a[e.vno][j]<INF && e.length+a[e.vno][j]<dist[j]) {//剪枝:e.vno到顶点j有边并且路径长度更短dist[j]=e.length+a[e.vno][j];prev[j]=e.vno;e1.vno=j; //建立相邻顶点j的结点e1e1.length=dist[j];qu.push(e1); //结点e1进队}}}
}

4.2.3旅行商问题

问题描述:一个商品推销员要去若干个城市推销商品,该 推销员从一个城市出发,需要经过所有城市后,回到出发地。 应如何选择行进路线,以使总的行程最短。

队列式分支限界法求解:

定义顶点结构:

struct Node {int t;    //顶点的深度int road[MAXN];   //当前路径int length;    //当前走过的总花费
};

代码如下:

#include<stdio.h>
#include<queue>
#define INF 0x3f3f3f3f
#define MAXN 51
using namespace std;int n=5;
/*int a[MAXN][MAXN]= {{INF,INF,INF,INF,INF},{INF,INF,30,6,4},{INF,30,INF,5,10},{INF,6,5,INF,20},{INF,4,10,20,INF}
};*/int a[MAXN][MAXN]= {{INF,INF,INF,INF,INF,INF},{INF,INF,13,3,7,2},{INF,13,INF,6,1,9},{INF,3,6,INF,8,16},{INF,7,1,8,INF,19},{INF,2,9,16,19,INF}
};
int bestx[MAXN];
int minlength=INF;
void dfs();
void output();struct Node {int t;    //顶点的深度int road[MAXN];   //当前路径int length;    //当前走过的总花费
};int main() {dfs();output();
}void dfs() {Node e;Node e1;int t;queue<Node> qu;for(int i=1; i<=n; i++)e1.road[i]=i;e1.t=2;e1.length=0;qu.push(e1);while(!qu.empty()) {e=qu.front();qu.pop();t=e.t;if(t==n) {if(a[e.road[t-1]][e.road[t]] != INF && a[e.road[t]][1] != INF) {if(e.length+a[e.road[t-1]][e.road[t]]+a[e.road[t]][1] < minlength) {minlength=e.length+a[e.road[t-1]][e.road[t]]+a[e.road[t]][1];for(int i=1; i<=n; i++)bestx[i]=e.road[i];}}continue;} else {if(e.length>=minlength)continue;for(int j=t; j<=n; j++) {if(a[e.road[t-1]][e.road[j]] != INF &&   e.length+a[e.road[t-1]][e.road[j]]<minlength) {e1.length=e.length+a[e.road[t-1]][e.road[j]];e1.t=t+1;for(int k=1; k<=n; k++)e1.road[k]=e.road[k];swap(e1.road[t],e1.road[j]);qu.push(e1);}}}}
}void output() {for(int i=1; i<=n; i++) {printf("%d->",bestx[i]);}printf("%d\n",bestx[1]);printf("最短路径长度为:%d",minlength);
}

这个代码当时我在写的时候,一直不理解为什么要swap,后来明白了是通过swap来交换次序得到不同的路线,最终得到最优解。

4.3总结

学好分支限界法,主要有以下方面:
1.确定限界函数

2.组织待处理活结点表

3.确定最优解中的各个分量

【算法】四、分支限界法相关推荐

  1. 使用PyTorch从零开始实现YOLO-V3目标检测算法 (四)

    原文:https://blog.csdn.net/u011520516/article/details/80228130 点击查看博客原文 这是从零开始实现YOLO v3检测器的教程的第4部分,在上一 ...

  2. java经典算法四十题

    java经典算法四十题 [程序9]题目:一个数如果恰好等于它的因子之和,这个数就称为 "完数 ".例如6=1+2+3.编程找出1000以内的所有完数. public class W ...

  3. 图解排序算法(四)之归并排序

    图解排序算法(四)之归并排序 基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide) ...

  4. java环形链表_数据结构和算法(四)Java实现环形链表

    1. 数据结构和算法(四)Java实现环形链表 1.1 约瑟夫问题 约瑟夫问题:公元66年,约瑟夫不情愿地参与领导了犹太同胞反抗罗马统治的起义,后来起义失败,他和一些宁死不降的起义者被困于一个山洞之中 ...

  5. 生产实践中的经典算法(四)-BitMap

    生产实践中的经典算法(四)-BitMap 1.BitMap的原理 位图(Bitmap),即位(Bit)的集合,是一种数据结构,可用于记录大量的0-1状态,在很多地方都会用到,比如Linux内核(如in ...

  6. 用OpenCV实现Photoshop算法(四): 色阶调整

    系列文章: 用OpenCV实现Photoshop算法(一): 图像旋转 用OpenCV实现Photoshop算法(二): 图像剪切 用OpenCV实现Photoshop算法(三): 曲线调整 用Ope ...

  7. 分支限界法 01背包c语言,算法笔记分支限界法01背包问题

    <算法笔记分支限界法01背包问题>由会员分享,可在线阅读,更多相关<算法笔记分支限界法01背包问题(12页珍藏版)>请在人人文库网上搜索. 1.问题描述给定n种物品和一背包.物 ...

  8. 趣学算法系列-分支限界法

    趣学算法系列-分支限界法 声明:本系列为趣学算法一书学习总结内容,在此推荐大家看这本算法书籍作为算法入门, 原作者博客链接,本书暂无免费电子版资源,请大家支持正版 第六章 分支限界法 在树搜索法中,从 ...

  9. java符号三角形问题_实验四 回溯算法和分支限界法 符号三角形问题

    基本题一:符号三角形问题 一.实验目的与要求 1.掌握符号三角形问题的算法: 2.初步掌握回溯算法: 二.实验题图 下面都是"-".下图是由14个"+"和14个 ...

  10. 算法------四数相加 II (java 版本)

    题目: 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0.为了使问题简单化,所有 ...

最新文章

  1. 如何构建可解释的推荐系统?| 深度
  2. LOL手游上线!同步专属限量游戏红包封面,还不快来拿?
  3. securecrt切换会话(session)的显示方式
  4. Haproxy + Pacemaker 实现高可用负载均衡(二)
  5. dubbo报错Data length too large: 10710120处理,及服务提供者协议配置详细说明
  6. VS2017社区版30天到期无法使用的激活方法
  7. 白盒测试中的六种覆盖方法及案例分析
  8. 数字逻辑要不要认真学_认真认真
  9. UVa12034 Race
  10. RTT Nano+STM32F407ZGT6+LAN8720A+LWIP+CubeMX+MDK
  11. linux多目录多域名,一个空间放多个网站(多域名绑定到同一空间不同目录)
  12. c#中Hashtable用法简述
  13. php生成成语,洪恩在线成语词典小偷程序php版_PHP教程
  14. 海康服务器协议,国标流媒体服务器GB28181协议和海康设备的交互过程记录
  15. css clac计算属性
  16. 2021微信大数据挑战赛总结(微信视频号推荐)
  17. IE高版本兼容低版本
  18. 关于Windows10右键新建卡顿
  19. 【死链】JDK1.7中HashMap在多线程环境的并发问题源码分析
  20. ASP.NET MVC5 实现基于Quartz.NET任务调度

热门文章

  1. Windows无法启动,Recovery Your PC/Device needs to be repaired,Boot\BCD 0Xc000000d
  2. java怎么用doss窗口_Java基础1-环境变量的配置
  3. C.哦~唔西迪西小姐~(贪心,stl)
  4. 牧牛区块链教育,区块链技术如何助力农村环保
  5. 重大喜讯┃Filenet与知名资本方达成深度战略合作
  6. 中奖名单 | 这个中秋不止月饼哦~
  7. 完整的IOCP模型 Echo服务器及代码分析
  8. JavaScript实现打砖块游戏_艾孜尔江撰
  9. 一篮子苹果,每天吃一半多一个吃,第十天吃一半多一个后就剩余一个,求一共多少个苹果,JAVA版...
  10. I9 9900K线程_地表最强无人撼动!i9-9900K仍是最强游戏CPU