【算法】四、分支限界法

这篇具有很好参考价值的文章主要介绍了【算法】四、分支限界法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

分支限界法(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();//出队列结点e
		for (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的结点e1
				e1.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.确定最优解中的各个分量文章来源地址https://www.toymoban.com/news/detail-756249.html

到了这里,关于【算法】四、分支限界法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 五种基础算法小结与典型题目分享(动态规划、分治、贪心、回溯、分支限界)

    动态规划是用于解决多阶段决策问题的算法策略。它通过用变量集合描述当前情境来定义“状态”,进而用这些状态表达每个阶段的决策。 每个阶段的状态是基于前面的状态经过某种决策得到的。通过建立状态间的递推关系,并将其形式化为数学递推式,得到“状态转移方程

    2024年01月19日
    浏览(49)
  • 【课设】java:迷宫小游戏(递归与分治、动态规划、贪心算法、回溯法、分支限界法)

    鱼弦:CSDN内容合伙人、CSDN新星导师、全栈领域优质创作者 、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen) 递归与分治算法 原理: 递归与分治算法将问题分解为子问题,递归地解决每个子问题,最后将结果合并得到整

    2024年02月02日
    浏览(28)
  • 06分支限界法

    分支限界法与回溯法的区别: (1) 求解目标不同 :回溯法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界法的求解目标则是找出满足约束条件的一个解,或是在满足约束条件的解中找出在某种意义下的最优解。 (2) 搜索方式的不同 :回溯法以 深度优先

    2024年02月06日
    浏览(30)
  • 实验九 分支限界法

    任务描述 本关任务:编写用分支限界法。 相关知识 为了完成本关任务,你需要掌握:分支限界法。 实验原理 , , 印刷电路板将布线区域分成nm个方格。其中绿色的方格是封锁的,即不能布线的方格。白色的方格是可以布线的。精确的电路布线问题要求确定连接方格a中点到方

    2024年02月12日
    浏览(32)
  • 算法 数据结构分类 数据结构类型介绍 数据结构线性非线性结构 算法合集 (一)

     数据结构分为:                            a.线性结构                            b.非线性结构  a.线性结构:                       数据与结构存在一对一的线性关系; a . 线性结构 存储 分为:                                   顺序存储

    2024年02月10日
    浏览(36)
  • N皇后问题(分支限界法)

    在 n * n 格的棋盘上放置彼此不受攻击的 n 个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题等价于在 n * n 的棋盘上放置 n 个皇后,任何 2个皇后不放在同一行或同一列或同一斜线上。 设计一个解 n 皇后问题的队列式分支

    2024年02月04日
    浏览(24)
  • 分支限界TSP(旅行商问题)

    【问题】 TSP 问题(traveling salesman problem) 是指旅行家要旅行 n 个城市, 要求各个城市经历且仅经历一次然后回到出发城市, 并要求所走的路程最短。 【想法】 首先确定目标函数的界[down, up], 可以采用贪心法确定 TSP 问题的一个上界。 如何求得 TSP 问题的一个合理的下界呢

    2024年02月08日
    浏览(30)
  • 【算法与数据结构】--算法应用--算法和数据结构的案例研究

    一、项目管理中的算法应用 在项目管理中,算法和数据结构的应用涉及项目进度、资源分配、风险管理等方面。以下是一些案例研究,展示了算法在项目管理中的实际应用: 项目进度管理 : 甘特图算法 :甘特图是一种项目进度管理工具,它使用甘特图算法来展示项目任务

    2024年02月08日
    浏览(41)
  • 数据结构与算法设计分析—— 数据结构及常用算法

    1、顺序表与链表 线性表是 线性结构 ,是包含n个数据元素的有限序列,通过顺序存储的线性表称为 顺序表 ,它是将线性表中所有元素按照其逻辑顺序,依次存储到指定存储位置开始的一块连续的存储空间里;而通过链式存储的 链表 中,每个结点不仅包含该元素的信息,还

    2024年02月07日
    浏览(43)
  • 分支限界法求0-1背包问题

    (1)画出解空间树 (2)Say如何剪枝 (3)求出最优解 假设物品的个数n=3,背包容量W = 30, 重量w =(16,15,15),价值v =(45,25,25) (1)队列式(FIFO)分支限界法:按照队列 先进先出 (FIFO)原则选取下一个结点为扩展结点。 (2)优先队列式分支限界法:按照优先队列中规定的

    2024年02月13日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包