IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 分治限界算法思想和应用 -> 正文阅读

[数据结构与算法]分治限界算法思想和应用

一、分支限界算法思想

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

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

2. 分治限界算法基本思想:

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

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

扩展节点:一个正在产生儿子的节点
活结点:一个自身已经生成,但其儿子还没有全部生成的节点
死结点:一个所有儿子已经产生的节点

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

从活节点表中选择下一个扩展节点时,不同的方式导致不同的分支限界法,常见有:
在这里插入图片描述

二、分支限界法的应用

1. 集装箱装载问题

有一批共n个集装箱要装上两艘载重量分别为c1,c2的轮船,其中集装箱i的重量为wi,且要求确定是否有一个合理的装载方案可将这n个集装箱装上这两艘轮船。

分析:
在这里插入图片描述
仅求出装载的最大重量

#include<iostream>
#include<queue>
using namespace std;

//描述节点类型
struct Node
{
	Node(int l, int w)
	{
		level = l;
		weight = w;
	}
	int level;//当前节点的层数
	int weight;//从根节点到当前节点所选物品的总重量
};
int main()
{
	int w[] = { 12,8,15 };//集装箱的重量
	int c = 27;//轮船的容量
	int n = sizeof(w) / sizeof(w[0]);//集装箱的数量
	int cw = 0;//已选物品的重量
	int bestw = 0;//记录最优装载量

	int i = 0;
	//广度优先遍历子集树的FIFO队列
	queue<Node> que;

	while (i < n)//当前节点是第i层
	{
		//处理左孩子,表示选择i节点
		if (cw + w[i] <= c)//选择i节点后,其总重量不能超过轮船的总重量
		{
			if (cw + w[i] > bestw)
			{
				bestw = cw + w[i];
			}
			que.push(Node(i + 1, cw + w[i]));//活结点孩子入队列
		}	
		//处理右孩子,表示不选择i节点
		que.push(Node(i + 1, cw));
		//处理完i节点后,它成为死节点,然后出队列
		Node node = que.front();
		que.pop();
		//恢复cw和i的值,表示从i节点跳到广度遍历的下一个节点
		cw = node.weight;
		i = node.level;
	}
	cout << bestw << endl;
}

打印出具体装入的集装箱

#include<iostream>
#include<queue>
using namespace std;

int w[] = { 12,8,15 };//集装箱的重量
int c = 27;//轮船的容量
const int n = sizeof(w) / sizeof(w[0]);//集装箱的数量
int cw = 0;//已选物品的重量
int bestw = 0;//记录最优装载量

//描述节点类型
struct Node
{
	Node(int w, int l, Node* p, bool left)
	{
		weight = w;
		level = l;
		parent = p;
		isleft = left;
	}
	int level;//当前节点的层数
	int weight;//从根节点到当前节点所选物品的总重量
	Node* parent;//记录当前节点的父节点
	bool isleft;//记录当前节点是否被选择
};

int i = 0;
Node* node = nullptr;
//广度优先遍历子集树的FIFO队列
queue<Node*> que;
Node* bestnode = nullptr;

//添加活结点到队列中
void addLiveNode(int w, int level, Node* parent, bool isleft)
{
	Node* node = new Node(w, level, parent, isleft);
	que.push(node);
	//在最后一层,记录最优节点
	if (level == n && w == bestw)
	{
		bestnode = node;
	}
}
int main()
{
	while (i < n)//当前节点是第i层
	{
		//处理左孩子,表示选择i节点
		if (cw + w[i] <= c)//选择i节点后,其总重量不能超过轮船的总重量
		{
			if (cw + w[i] > bestw)
			{
				bestw = cw + w[i];
			}
			addLiveNode(cw + w[i], i + 1, node, true);
		}	
		//处理右孩子,表示不选择i节点
		addLiveNode(cw, i + 1, node, false);
		//处理完i节点后,它成为死节点,然后出队列
		node = que.front();
		que.pop();
		//恢复cw和i的值,表示从i节点跳到广度遍历的下一个节点
		cw = node->weight;
		i = node->level;
	}
	cout << bestw << endl;
	int bestx[n] = { 0 };
	for (int j = n - 1; j >= 0; j--)
	{
		bestx[j] = bestnode->isleft ? 1 : 0;
		bestnode = bestnode->parent;
	}
	for (int v : bestx)
	{
		cout << v << " ";
	}
}

添加剪枝操作,限界

#include<iostream>
#include<queue>
using namespace std;

int w[] = { 12,8,15 };//集装箱的重量
int c = 27;//轮船的容量
const int n = sizeof(w) / sizeof(w[0]);//集装箱的数量
int cw = 0;//已选物品的重量
int bestw = 0;//记录最优装载量
int r = 0;

//描述节点类型
struct Node
{
	Node(int w, int l, Node* p, bool left)
	{
		weight = w;
		level = l;
		parent = p;
		isleft = left;
	}
	int level;//当前节点的层数
	int weight;//从根节点到当前节点所选物品的总重量
	Node* parent;//记录当前节点的父节点
	bool isleft;//记录当前节点是否被选择
};

int i = 0;
Node* node = nullptr;
//广度优先遍历子集树的FIFO队列
queue<Node*> que;
Node* bestnode = nullptr;

//添加活结点到队列中
void addLiveNode(int w, int level, Node* parent, bool isleft)
{
	Node* node = new Node(w, level, parent, isleft);
	que.push(node);
	//在最后一层,记录最优节点
	if (level == n && w == bestw)
	{
		bestnode = node;
	}
}

int maxBound(int level)
{
	int s = 0;
	for (int j = level + 1; j < n; j++)
	{
		s += w[i];
	}
	return s;
}
int main()
{
	while (i < n)//当前节点是第i层
	{
		//处理左孩子,表示选择i节点
		if (cw + w[i] <= c)//选择i节点后,其总重量不能超过轮船的总重量
		{
			if (cw + w[i] > bestw)
			{
				bestw = cw + w[i];
			}
			addLiveNode(cw + w[i], i + 1, node, true);
		}	
		//处理右孩子,表示不选择i节点
		r = maxBound(i);//求第i个节点的重量值上界
		if (cw + r >= bestw)//>=这里的=不能少,否则无法选择到叶子节点上c=20
		{
			addLiveNode(cw, i + 1, node, false);
		}
		//处理完i节点后,它成为死节点,然后出队列
		node = que.front();
		que.pop();
		//恢复cw和i的值,表示从i节点跳到广度遍历的下一个节点
		cw = node->weight;
		i = node->level;
	}
	cout << bestw << endl;
	int bestx[n] = { 0 };
	for (int j = n - 1; j >= 0; j--)
	{
		bestx[j] = bestnode->isleft ? 1 : 0;
		bestnode = bestnode->parent;
	}
	for (int v : bestx)
	{
		cout << v << " ";
	}
}

2. 0-1背包问题

  • FIFO队列
    在这里插入图片描述
#include<iostream>
#include<queue>
using namespace std;

int w[] = { 16,15,15 };
int v[] = { 45,25,25 };
int c = 30;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
int bestv = 0;

struct Node
{
	Node(int w, int v, int l, Node* p, bool left)
	{
		weight = w;
		value = v;
		level = l;
		parent = p;
		isleft = left;
	}
	int weight;
	int value;
	int level;
	Node* parent;
	bool isleft;
};
int i = 0;
queue<Node*> que;
Node* bestnode = nullptr;
void addLiveNode(int w, int v, int l, Node* p, bool left)
{
	Node* node = new Node(w, v, l, p, left);
	que.push(node);
	if (l == n && v == bestv)
	{
		bestnode = node;
	}
}
int maxBound(int i)
{
	int s = 0;
	for (int j = i + 1; j < n; j++)
	{
		s += v[i];
	}
	return s;
}
int main()
{
	Node* node = nullptr;
	while (i < n)
	{
		if (cw + w[i] <= c)
		{
			if (cv + v[i] > bestv)
			{
				bestv = cv + v[i];
			}
			addLiveNode(cw + w[i], cv + v[i], i + 1, node, true);
		}
		int r = maxBound(i);
		if (cv + r >= bestv)
		{
			addLiveNode(cw, cv, i + 1, node, false);
		}
		node = que.front();
		que.pop();
		cw = node->weight;
		cv = node->value;
		i = node->level;
	}
	int bestx[n] = { 0 };
	for (int j = n - 1; j >= 0; j--)
	{
		bestx[j] = bestnode->isleft ? 1 : 0;
		bestnode = bestnode->parent;
	}
	cout << bestv << endl;
	for (int v : bestx)
	{
		cout << v << " ";
	}
}
  • 优先级队列
#include<iostream>
#include<queue>
#include<functional>
using namespace std;

int w[] = { 16,15,15 };
int v[] = { 45,25,25 };
int c = 30;
const int n = sizeof(w) / sizeof(w[0]);
int cw = 0;
int cv = 0;
int bestv = 0;

struct Node
{
	Node(int w, int v, int l, int up, Node* p, bool left)
	{
		weight = w;
		value = v;
		level = l;
		upbound = up;
		parent = p;
		isleft = left;
	}
	int weight;
	int value;
	int level;
	int upbound;//节点的价值上界,从这个节点往下,最多能选择的物品产生的总价值
	Node* parent;
	bool isleft;
};
int i = 0;
priority_queue<Node*, vector<Node*>, function<bool(Node*, Node*)>> que([](Node* n1, Node* n2)->bool {
	return n1->upbound < n2->upbound;
	});
void addLiveNode(int w, int v, int l, int up, Node* p, bool left)
{
	Node* node = new Node(w, v, l, up, p, left);
	que.push(node);
}
//求价值上界
int maxBound(int i)
{
	int s = cv;
	for (int j = i; j < n; j++)
	{
		s += v[i];
	}
	return s;
}
int main()
{
	Node* node = nullptr;
	int upbound = maxBound(0);
	while (i < n)
	{
		if (cw + w[i] <= c)
		{
			if (cv + v[i] > bestv)
			{
				bestv = cv + v[i];
			}
			addLiveNode(cw + w[i], cv + v[i], i + 1, upbound, node, true);
		}
		//不选择物品i
		upbound = maxBound(i + 1);//i+1表示第一个未被处理的物品的下标
		if (upbound >= bestv)
		{
			addLiveNode(cw, cv, i + 1, upbound, node, false);
		}
		node = que.top();
		que.pop();
		cw = node->weight;
		cv = node->value;
		i = node->level;
		upbound = node->upbound;
	}
	int bestx[n] = { 0 };
	for (int j = n - 1; j >= 0; j--)
	{
		bestx[j] = node->isleft ? 1 : 0;
		node = node->parent;
	}
	cout << bestv << endl;
	for (int v : bestx)
	{
		cout << v << " ";
	}
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:24:47  更:2022-04-15 00:26:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 7:49:06-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码