算法设计与分析/数据结构与算法实验7:0-1背包问题(分支限界法)

简介: 算法设计与分析/数据结构与算法实验7:0-1背包问题(分支限界法)

1.实验目的

 (1)掌握分支限界法的处理思路与算法框架。

 (2)掌握应用分支限界法解决具体问题的方法。

 (3)掌握分支限界法的广泛应用。

2.实验内容

(1)问题描述

image.png


 要求使用分支限界法解决该问题。

(2)输入

 输入包含3行。

image.png


(3)输出

image.png


3.问题实例分析

image.png


 堆(优先队列)是大根堆,“优先级”即判断函数为价值上界。

 此时大根堆堆顶为(“拿1,上界14.33”)。对其进行扩展,以是否拿2作为区分的依据。拿2时,价值上界为14.33。不拿2时,价值上界为14。将这两个结点都加入堆(优先队列)。

 此时大根堆堆顶为“拿2,上界14.33”。对其进行扩展,以是否拿3作为区分依据。拿3时,价值上界为14.33。不拿3时,价值上界为14。将这两个结点都加入堆(优先队列)。

 此时大根堆堆顶为“拿3,上界14.33”。对其进行扩展,以是否拿4作为区分依据。拿4时,背包此时体积超过上限,不可行。不拿4时,价值上界为14.25。将可行的右结点加入堆(优先队列)。

 此时大根堆堆顶为“不拿4,上界14.25”。对其进行扩展,以是否拿5作为区分依据。拿5时,背包此时体积超过上限,不可行。不拿5时,价值上界为13。将可行的右结点加入堆(优先队列)。

 此时大根堆堆顶为“不拿3,上界14”。对其进行扩展,以是否拿4作为区分依据。拿4时,价值上界为14。不拿4时,价值上界为10。这两个结点都可行,都加入堆(优先队列)。

 此时大根堆堆顶为“拿4,上界14”。对其进行扩展,以是否拿5作为区分依据。拿5时,价值上界为14。不拿5时,价值上界为9。这两个结点都可行,都加入堆(优先队列)。

image.png

4.算法描述及说明

 正如第3节问题实例分析所述,算法的整体流程如下:

 1.输入数据,并对每个物品进行编号。

 2.计算每个物品的单位价值,并将物品按单位价值排序。

 3.计算根结点上界,并将根结点加入大根堆。

 4.取出堆顶元素。若堆顶结点为解空间树的叶结点,则算法直接结束,叶结点的物品选取信息和价值为所求的最大价值。

 5.若堆顶元素不是叶结点,则尝试扩展左结点。若左结点可行(选取该物品背包体积没有被超出),则对左结点计算上界后入堆。扩展右结点,不选取物品,对右结点计算上界后入堆。

5.算法正确性分析

 算法会正确地结束:在利用优先队列遍历解空间后,找到了使总价值最大的解,算法会停止。

 分支限界的正确性分析:开始时,根结点是解空间树唯一的活结点,也是当前的扩展结点。算法会不断扩展结点,直到子集树的一个叶结点成为扩展结点时为止。此时优先队列中所有活结点的价值上界不超过该叶结点的价值。因此,该叶结点对应的解为问题最优解。

 因此,利用分支限界法会系统地查找背包问题的所有可行解,利用限界函数剪去了不可行的分支,保留了可行并能产生最大解的分支。

 从而,该算法是正确的。

6.算法时间复杂性分析

image.png


7.运行结果展示及其说明

image.png

8.心得体会

9.程序源代码

#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
double cw;//当前重量
double cp;//当前价值
double bestp;//当前最优价值
int n;//物品数量
double c;//背包容量
const int N = 105;
struct Bag {
  double w, v;
  int id, x;
};
int bestx[N];
Bag bag[N];
struct BBnode {
  BBnode *parent;
  bool leftChild;
  BBnode(BBnode* par, bool ch) {
    parent = par;
    leftChild = ch;
  }
};
struct heapnode {
  BBnode* livenode;
  double upperprofit;//价值上界
  double profit;//结点价值
  double weight;//结点重量
  int level;//层序号
  bool operator<(const heapnode& b) const {
    return this->upperprofit < b.upperprofit;
  }
  heapnode(BBnode *node,double up, double pp, double ww, int lev) {
    livenode = node;
    upperprofit = up;
    profit = pp;
    weight = ww;
    level = lev;
  }
};
bool cmp(Bag a, Bag b) {
  return (a.v / a.w) > (b.v / b.w);
}
bool cmpbound(heapnode a, heapnode b) {
  return a.upperprofit < b.upperprofit;
}
priority_queue<heapnode, vector<heapnode>,less<heapnode> > q;
void addlivenode(double up, double pp, double ww, int lev, BBnode* par, bool ch) {
  BBnode* b = new BBnode(par, ch);
  heapnode node(b, up, pp, ww, lev);
  q.push(node);
}
double bound(int i) {
  double cleft = c - cw;
  double bd = cp;
  while (i <= n && bag[i].w <= cleft) {
    cleft -= bag[i].w;
    bd += bag[i].v;
    i++;
  }
  if (i <= n)
    bd += bag[i].v * cleft / bag[i].w;
  return bd;
}
void bfs() {
  BBnode *enode = NULL;
  int i = 1;
  bestp = 0;
  double up = bound(1);
  while (i != n + 1) {
    double wt = cw + bag[i].w;
    if (wt <= c) {
      if (cp + bag[i].v > bestp)
        bestp = cp + bag[i].v;
      addlivenode(up, cp + bag[i].v, cw + bag[i].w, i + 1, enode, true);
    }
    up = bound(i + 1);
    if (up > bestp)
      addlivenode(up, cp, cw, i + 1, enode, false);
    heapnode node = q.top();
    q.pop();
    enode = node.livenode;
    cw = node.weight;
    cp = node.profit;
    up = node.upperprofit;
    i = node.level;
  }
  for (int j = n; j > 0; j--) {
    bag[j].x = (enode->leftChild!=NULL) ? 1 : 0;
    enode = enode->parent;
  }
  for (int i = 1; i <= n; i++)
    bestx[bag[i].id] = bag[i].x;
  return;
}
int main() {
  cin >> n >> c;
  for (int i = 1; i <= n; i++)
    cin >> bag[i].w;
  for (int i = 1; i <= n; i++)
    cin >> bag[i].v;
  for (int i = 1; i <= n; i++)
    bag[i].id = i;
  sort(bag + 1, bag + 1 + n, cmp);
  bfs();
  cout << bestp << endl;
  for (int i = 1; i <= n; i++)
    cout << bestx[i] << " ";
  return 0;
}


目录
相关文章
|
7月前
|
机器学习/深度学习 算法 数据挖掘
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构
K-means聚类算法是机器学习中常用的一种聚类方法,通过将数据集划分为K个簇来简化数据结构。本文介绍了K-means算法的基本原理,包括初始化、数据点分配与簇中心更新等步骤,以及如何在Python中实现该算法,最后讨论了其优缺点及应用场景。
432 6
|
7月前
|
存储 算法 编译器
数据结构实验之矩阵的运算器(二维数组)
本实验旨在通过团队合作,掌握数组和矩阵相关运算的代码实现,包括矩阵的加减、数乘、转置、乘法、n次方及行列式的计算。实验过程中,成员们需分工协作,解决编程难题,最终实现一个功能完备的矩阵计算器。通过本实验,不仅锻炼了编程能力,还加深了对数学概念的理解,同时培养了团队合作精神。
136 4
|
7月前
数据结构实验之串模式匹配问题
本实验旨在掌握串模式匹配技术,通过创建文本文件、实现单词计数与定位功能,最终构建一个包含文件建立、单词统计与定位、程序退出等选项的主菜单,以增强对字符串处理的理解与应用能力。
126 4
|
7月前
|
算法
数据结构实验之最长公共子序列
本实验旨在通过编程实践帮助学生理解串的基本概念及求解最长公共子序列的算法。实验内容包括使用动态规划方法设计并实现算法,以找出给定两序列的最大公共子序列。示例代码展示了如何通过构建状态矩阵和回溯路径来找到解决方案。实验总结指出,`memset()`函数用于内存初始化,且对于特定输入,程序能正确输出最长公共子序列之一。
129 4
|
7月前
|
算法
数据结构实验之操作系统打印机管理器问题
本实验旨在通过实现操作系统中的打印机管理器问题,掌握队列的基本操作如入队、出队等,利用队列的先进先出特性解决先申请先打印的问题。实验包括队列的初始化、入队、出队、打印队列内容等功能,并通过菜单式界面进行交互。实验结果显示基本功能可正常执行,但在连续操作时存在执行失败的情况,需进一步优化。
113 4
|
7月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
126 4
|
3月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
95 9
 算法系列之数据结构-二叉树
|
3月前
|
算法 Java
算法系列之数据结构-Huffman树
Huffman树(哈夫曼树)又称最优二叉树,是一种带权路径长度最短的二叉树,常用于信息传输、数据压缩等方面。它的构造基于字符出现的频率,通过将频率较低的字符组合在一起,最终形成一棵树。在Huffman树中,每个叶节点代表一个字符,而每个字符的编码则是从根节点到叶节点的路径所对应的二进制序列。
107 3
 算法系列之数据结构-Huffman树
|
3月前
|
算法 Java
算法系列之数据结构-二叉搜索树
二叉查找树(Binary Search Tree,简称BST)是一种常用的数据结构,它能够高效地进行查找、插入和删除操作。二叉查找树的特点是,对于树中的每个节点,其左子树中的所有节点都小于该节点,而右子树中的所有节点都大于该节点。
102 22
|
4月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
136 29