目录

  • 一、分支限界算法思想
    • 1. 分支限界法类似于回溯算法,是在问题的解空间树上搜索问题的算法,主要体现在两点不同:
    • 2. 分治限界算法基本思想:
  • 二、分支限界法的应用
    • 1. 集装箱装载问题
    • 2. 0-1背包问题

一、分支限界算法思想

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);}//不选择物品iupbound = 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 << " ";}
}

分治限界算法思想和应用相关推荐

  1. 经典五大算法思想-------入门浅析

    算法:求解具体问题的步骤描述,代码上表现出来是解决特定问题的一组有限的指令序列. 1.分治: 算法思想:规模为n的原问题的解无法直接求出,进行问题规模缩减,划分子问题(这里子问题相互独立而且和原问题解 ...

  2. 常见的算法思想(整理)

    1.算法特征 算法的英文名称是Algorithm,这个词在1957年之前在Webster's New World Dictionary(<韦氏新世界词典>)中还未出现,只能找到带有它的古代 ...

  3. Algorithms_算法思想_递归分治

    文章目录 引导案例 递归的定义 什么样的问题可以用递归算法来解决 递归如何实现以及包含的算法思 递归的公式 斐波那契数列代码实现 递归的时间复杂度和空间复杂度 递 与 归 递归的优化 优化方式一:不使 ...

  4. 【数据结构与算法】【算法思想】【联系与区别】回溯 贪心 动态规划 分治

    4种算法思想比较与联系 如果将贪心,分治,回溯和动态规划四种算法思想分类,那贪心,回溯,动态规划可归为一类,而分治单独可以作为一类,因为它跟其他是三个都不大一样. 因为前三个算法解决问题的模型,都可以 ...

  5. 分治法的关键特征_经典算法思想2——分治(Divide-and-Conquer)

    分治法,字面意思是"分而治之",就是把一个复杂的1问题分成两个或多个相同或相似的子问题,再把子问题分成更小的子问题直到最后子问题可以简单地直接求解,原问题的解即子问题的解的合并,这 ...

  6. 重建二叉树(分治算法思想)

    输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点. 假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 示例 1: Input: preorder = [3,9,20,15,7 ...

  7. c语言动态规划回溯的原理,「算法思想」分治、动态规划、回溯、贪心一锅炖...

    观感度:????? 口味:东北一锅出 烹饪时间:10min 本文已收录在Github github.com/Geekhyt,感谢Star. 数据结构与算法系列专栏第四弹来袭,往期专栏链接如下: 初学者 ...

  8. 五大算法思想(一)分治算法及常见例子

    文章目录 一.理论基础 1.1 适用场景 1.2 使用步骤 1.3 经典例子 二.常见例子 2.1 二分搜索 2.2 大整数乘法 2.3 Strassen矩阵乘法 2.4 棋盘覆盖 2.5 合并排序 ...

  9. 动态规划从理论到实践-深入理解贪心/分治/回溯/动态规划的算法思想

    摘要:本文主要讲解常见的四种算法,分别为贪心算法:第一步选择最优的走法,算法不能做到全局最优:分治算法:一种处理问题的思想,使用递归来实现:回溯算法:我们枚举所有的解,找到满足期望的解,可以把求解过程 ...

最新文章

  1. C语言入门练习 - 第二期 判断语句与循环语句(题解)
  2. phython在file同时写入两个_喜大普奔,两个开源的 Spring Boot + Vue 前后端分离项目可以在线体验了
  3. Centos7开启SSH服务
  4. DIY人脸跟踪电风扇送女朋友(1)
  5. Python面试题目--汇总
  6. matlab常用函数——数据类型函数
  7. .net程序员安全注意代码及服务器配置
  8. (软件工程复习核心重点)第十二章软件项目管理-第四节:软件配置管理和能力成熟度模型
  9. 【机器学习技术】高斯过程初探
  10. 方便的Chrome取色插件ColorPick Eyedropper [设计, FE必备]
  11. laravel 向模板中添加公共变量
  12. mysql数据库字段字符转数字批量语句_MySQL数据库批量替换指定字段字符串SQL语句命令...
  13. python相关参考文献_深度学习自然语言处理综述,266篇参考文献
  14. 视频编辑软件(Nero Video2021中文版) v23.0.1.12pjb
  15. Redis客户端工具-AnotherRedisDesktopManager
  16. 微信公众号订阅号与微信服务号区别
  17. 计算机色温调整,电脑色温如何调节
  18. MS SQL2000个人版安装教程(图文教程)
  19. 这个开源的去马赛克神器 修复受损漫画无压力
  20. 绝地求生渠道和用户画像分析

热门文章

  1. arcgis九段线、南海诸岛
  2. 《炬丰科技-半导体工艺》氧化铝陶瓷基板上的紫外激光微加工和化学蚀刻
  3. jQuery05(插件)
  4. DVWA靶场Brute Force 暴力破解审计通关教程
  5. 【Shell】在windows下编辑shell脚本
  6. CMD(命令提示符)修改盘符
  7. 【计算机毕业设计】旅游网站ssm源码
  8. 男女交往技巧 男生如何赞美女生才有效
  9. c++基础-继承与派生,定义基类person和公有派生类student
  10. ubuntu如何在多个工作区之间自由切换以及添加返回桌面图标