蓝桥杯之分支限界算法

算法思想

分支限界算法类似于回溯算法,是在问题的解空间树上搜索问题的解的算法,主要体现在两点不同:

1,求解的目标不同。回溯算法的求解目标是找出解空间树中满足约束条件的所有解,而分支限界的求解目标是找出满足约束条件的一个解,或者是在满足约束条件的解中找出某种意义上的最优解

2,搜索解空间树的方式不同。回溯算法以深度优先搜索解空间树,而分支限界是以广度优先搜索(以最小耗费优先)

分支限界法长以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。在分支限界法中每一个活结点只有一次机会称为扩展节点,活结点一旦成为扩展节点,就一次性产生其所有的儿子节点(分支),在这些儿子节点中,导致不可行解或是导致非最优解的儿子节点会被舍弃掉,其余儿子节点会被加入活结点列表中。

为了有效的选择下一个扩展节点加速搜索,在每一个活结点处计算一个函数值(限界),并根据计算的函数值结果从当前活结点表中取下一个最有利的节点成为当前的扩展节点,是搜索朝着解空间树上最优解的分支推进。重复上述节点扩展过程,直到找到所需的最优解或者活结点表为空

扩展节点:一个正在产生儿子的节点称作扩展节点

活结点:一个自身已经产生但其儿子还没有生成

死节点:一个所有儿子已经产生的节点

深度优先搜索是对一个扩展节点R,一旦产生了一个儿子C,就把C当做新的扩展节点。在完成对子树C的深度优先搜索之后回溯到R时,将重新变成扩展节点,继续生成R的下一个儿子

广度优先搜索是在一个扩展节点R变成死节点之前,它一直都是扩展节点

从活结点表中选择下一个扩展节点时,不同的方式会导致不同的分支限界法:

1,普通队列式

a,一开始根节点是唯一的活结点,根节点加入活结点队列

b,从活结点队列中取出对头节点后,作为当前扩展节点

c,对当前扩展节点,先从左到右产生它的所有孩子节点,用约束条件检查,把所有满足约束函数的孩子节点加入活结点队列中

d,再从活结点表中取出队首节点为当前扩展节点,重复上述过程,直到找到一个解或活结点队列为空为止。

2,优先级队列分支限界法

a,对每一个活结点计算一个优先级(某些信息的函数值)

b,根据这些优先级从当前活结点表中优先选择一个优先级最高(最有利)的节点作为扩展节点,是搜索朝着解空间树上有最优解的分支推进,以便尽快找出一个最优解

c,对当前扩展节点,先从左到右产生它的所有孩子节点,用约束条件检查,对所有满足约束函数的孩子节点计算优先级并加入到活结点优先级队列中。

d,再从活节点表中取出下一个优先级最高的节点为当前扩展节点,重复上述过程,直到找到一个解或者队列为空为止 。

集装箱装载问题

cpp 复制代码
int w[] = { 12,8,15 };//集装箱货物的重量
const int len = sizeof(w) / sizeof(w[0]);
int c = 20;//轮船的容量
int cw = 0;//已选择物品的重量
int bestw = 0;//记录最优的装载量
//分支限界算法
int r = 0;//表示未选择的重量
//先创建节点
struct Node
{
	Node(int w, int i, Node* parent, bool isleft)
	{
		this->w = w;
		this->i = i;
		this->parent = parent;
		this->isleft = isleft;
	}
	int w;//当前节点的重量
	int i;//是为了记录子节点
	//这两个主要是为了方便找出那个节点被选为最优解
	Node* parent;
	bool isleft;
};
queue <Node*> que;
Node* bestNode = NULL;
void addNode(int w, int i, Node* parent, bool isleft)
{
	Node* newnode = new Node(w, i, parent, isleft);
	que.push(newnode);
	if (i == len && w == bestw)
	{
		bestNode = newnode;
	}
}
int maxR(int level)
{
	int j=0;
	for (int i = level; i < len; i++)
	{
		j += w[i];
	}
	return j;
}
int main()
{
	Node* node = nullptr;
	int i = 0;
	while (i < len)
	{
		//选择i节点物品
		int wt = cw + w[i];
		if (wt <= c)
		{
			if (wt > bestw)
			{
				bestw = wt;
			}
			//将该节点的字节点放入队列中,左孩子
			addNode(wt, i + 1, node, true);
		}
		//再将右孩子放入队列中
		r = maxR(i+1);
		if(cw+r>=bestw)
			addNode(cw, i + 1, node, false);

		//开始出队列去更新i和cw
		node = que.front();
		que.pop();
		i = node->i;
		cw = node->w;
	}
	cout << bestw<<endl;
	int dex[3] = { 0 };
	for (int j = 2; j >= 0; j--)
	{
		dex[j] = bestNode->isleft == true ? 1 : 0;
		bestNode = bestNode->parent;
	}
	for (int v : dex)
	{
		cout << v << " ";
	}
	return 0;
}

背包问题(优先级队列)

cpp 复制代码
int w[] = { 16,15,15 };//物品的重量
int v[] = { 45,25,25 };//物品的价值
int c = 30;//背包的容量
struct Node
{
	Node(int w, int v, int i, Node* parent, bool isleft,int upbound)
	{
		this->i = i;
		this->isleft = isleft;
		this->parent = parent;
		this->v = v;
		this->w = w;
		this->upbound = upbound;
	}
	int w;
	int v;
	int i;
	Node* parent;//用来记录父节点,方便回溯打印最优解
	bool isleft;//用来记录是否向左走(是否被选择)
	int upbound;//其实就是upbound越大,越先出队列
};
#include<functional>
int bestv = 0;
int len = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
class MyCompare
{
public:
	bool operator()(Node* n1,Node*n2)
	{
		return n1->upbound < n2->upbound;
	}
};
//priority_queue<Node*, vector<Node*>, function<bool(Node*, Node*)>>que([](Node* n1, //Node* n2)->bool {return n1->upbound < n2->upbound; });
priority_queue<Node*,vector<Node*>,MyCompare>que;
void addNode(int w, int v, int i, Node* node, bool isleft,int upbound)
{
	Node* newnode = new Node(w, v, i, node, isleft, upbound);
	que.push(newnode);
}
int maxBound(int i)
{
	int n = cv;
	for (int j = i; j < len; j++)
	{
		n += v[j];
	}
	return n;
}
int main()
{
	int i = 0;
	Node* node = nullptr;
	int upbound = maxBound(0);
	while (i < len)
	{
		int wt = cw + w[i];
		if (wt <= c)
		{
			if (cv + v[i] > bestv)
			{
				bestv = cv + v[i];
			}
			addNode(wt, cv + v[i], i + 1, node, true, upbound);
		}
		upbound = maxBound(i + 1);
		if (maxBound(i + 1) >= bestv)
			addNode(cw, cv, i + 1, node, false,upbound);
		node = que.top();
		que.pop();
		i = node->i;
		cw = node->w;
		cv = node->v;
		upbound = node->upbound;
	}
	cout << bestv << endl;
	int x[3] = { 0 };
	for (int j = 2; j >= 0; j--)
	{
		x[j] = node->isleft == true ? 1 : 0;
		node = node->parent;
	}
	for (int v : x)
	{
		cout << v << " ";
	}
	return 0;
}
相关推荐
Dizzy.51738 分钟前
数据结构(查找)
数据结构·学习·算法
hello_simon2 小时前
【Word转PDF】在线Doc/Docx转换为PDF格式 免费在线转换 功能强大好用
职场和发展·pdf·word·学习方法·word转pdf·石墨文档·word转换
分别努力读书3 小时前
acm培训 part 7
算法·图论
武乐乐~3 小时前
欢乐力扣:赎金信
算法·leetcode·职场和发展
'Debug4 小时前
算法从0到100之【专题一】- 双指针第一练(数组划分、数组分块)
算法
Fansv5874 小时前
深度学习-2.机械学习基础
人工智能·经验分享·python·深度学习·算法·机器学习
夏天的阳光吖4 小时前
C++蓝桥杯基础篇(四)
开发语言·c++·蓝桥杯
测试19985 小时前
接口测试工具:Postman
自动化测试·软件测试·python·测试工具·职场和发展·接口测试·postman
yatingliu20195 小时前
代码随想录算法训练营第六天| 242.有效的字母异位词 、349. 两个数组的交集、202. 快乐数 、1. 两数之和
c++·算法
uhakadotcom6 小时前
Google DeepMind最近发布了SigLIP 2
人工智能·算法·架构