01背包问题-队列分支限界法-C++

news2025/2/25 8:39:54

0-1背包问题-队列分支限界法

问题描述:

给定n种物品和一个背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大?对于给定的n种物品的重量和价值,以及背包的容量,计算可装入背包的最大价值。

算法设计过程:

考虑使用队列分支限界法来解决来解决0-1背包问题,对于每一个物品,我们都有选或者不选两种情况,因此该问题的解空间为一颗子集树。

要采用队列来求解该问题,对于队列中的每一个节点,都要维护当前节点所选择的物品集合,同时维护当前节点的总价值和容量,为剪去不可行解和优化搜索过程做准备。

对于该问题,我们在使用队列分支限界的搜索过程,在处理当前节点时,可以考虑对左右儿子进行约束和限界:
对于该节点的左儿子,我们要考虑其是否满足约束,具体的,我们要考虑当前背包剩余空间能否装下这个物品,也就是当前的总重加上该物品的总量要小于背包总容量时我们才考虑进入左儿子节点。否则左儿子就是一个不可行解,直接跳过即可。
对于该节点的右儿子,我们考虑设计一个限界函数bound来限制进入右儿子的条件,在搜索过程我们维护一个当前的最优解,我们要通过计算进入右子树后有没有可能得到更优的解,只有右儿子包含最优解时我们才选择进入。具体的,设cv是当前的价值,我们考虑将该物品跳过后的最优价值为r,对于当前的最优解bstv,若是\cv+r<=bstv时,我们便考虑剪去右子树,为了更好的实现,我们需要在搜索之前对物品按单位重量价值进行排序,然后依次装入物品,直到装不下时,再装入该物品的一部分而装满背包,由此得到右子树的上界bound。

对于该问题我们的具体算法流程:
1.对所有物品按单位重量价值从大到小进行排序。
2.初始化队列,加入初始的节点,这里以第一个物品作为根节点加入。
3.当队列不为空时,每次从队列中取出一个节点的进行扩展。
4.如果左儿子是可行节点,即满足约束条件时,如果该结点的价值大于最优解,则更新最优解,同时将该节点加入队列。
5.对于扩展节点的右儿子,当其满足限界函数时才将其加队列中。
6.当队列为空时,说明搜索过程已经结束,直接返回最优解即可。

流程图:
在这里插入图片描述

代码:

#include <bits/stdc++.h>
using namespace std;

struct Obj{ // 物品
    int id; 
    int w;
    int val;
    bool operator<(Obj &obj){
        return val * obj.w > obj.val * w;
    }
};

struct Node {
    int dep; // 深度,第几层就是处理第几个物品
    int cv; /// 当前价值
    int cw; // 当前容量
    vector<int>x; // 解向量
    
    Node(int dep,int cv, int cw, vector<int>x):dep(dep), cv(cv), cw(cw), x(x) {} 

    friend ostream& operator<<(ostream& os, const Node& p){
        cout << "dep:" << p.dep << " cv:" << p.cv << " cw:" << p.cw;
        cout << " x:";
        for(int i:p.x)cout << i << ' ';
        return os;
    };  

};

struct Whopxx{
    int n; // 物品数量
    vector<Obj>vt; // 物品
    int sum; // 背包大小

    vector<int>bstx; // 最优解
    int bstv;
    
    Whopxx(int n,int sum, vector<int>w, vector<int>p):n(n), sum(sum) {
        bstx.resize(n + 1);
        bstv = 0;
        vt.resize(n + 1);
        for(int i = 1; i <= n; i++){
            vt[i] = {i, w[i], p[i]};
        }
        sort(vt.begin() + 1, vt.end()); // 按单价从大到小排序
    }

    void work(){
        queue<Node> q;
        q.push(Node(1, 0, 0, {})); // 第一个物品开始做选择
        while(q.size()){
            Node node = q.front(); q.pop();
            cout << node << " bound:" << Bound(node) << " bstv:" << bstv << endl;
            int i = node.dep;
            if(i > n || node.cw == sum){ // 到达叶子结点或背包已满
                if(node.cv > bstv) update(node); // 更新最大值
                continue;
            }
            
            auto [id, w, val] = vt[i];
            if(node.cw + w <= sum){ // 左儿子满足约束
                Node lnode = add(node, vt[i]);
                q.push(lnode);
                if(lnode.cv > bstv) update(lnode);
            }

            if(Bound(node) > 1.0 * bstv){ // 右儿子满足限界
                Node rnode = uadd(node);
                q.push(rnode);
            }
            
        }
    }

    double Bound(Node node){ // 限界函数
        int rw = sum - node.cw; // 剩余容量
        int i = node.dep + 1;
        double b = node.cv;
        while(i <= n && vt[i].w <= rw){
            rw -= vt[i].w;
            b += vt[i].val;
            i++;
        }
        if(i <= n){
            b += vt[i].val * 1.0 / vt[i].w * rw;
        }
        return b;
    }

    Node add(Node node, Obj obj){
        node.cv += obj.val;
        node.cw  += obj.w;
        node.x.push_back(obj.id);
        node.dep += 1;
        return node;
    }

    Node uadd(Node node){
        node.dep += 1;
        return node;
    }

    void update(Node node){
        bstx = node.x;
        bstv = node.cv;
    }
};


int main(){ 
    freopen("input.txt","r", stdin);
    freopen("output.txt", "w", stdout);
    
    int n, c; // 物品数量,背包容量
    cin >> n >> c;
    vector<int>w(n + 1);
    vector<int>p(n + 1);
    for(int i = 1; i <= n; i++) cin >> p[i];
    for(int i = 1; i <= n; i++) cin >> w[i];

    Whopxx wx(n, c, w, p);
    wx.work();
    vector<int>ans(n + 1);
    for(int i:wx.bstx) ans[i] = 1;
    cout << wx.bstv << endl;
    for(int i = 1; i <= n; i++){
        cout << ans[i] << ' ';
    }

    fclose(stdin);
    fclose(stdout);
    return 0;
}
/*
4 7
9 10 7 4
3 5 2 1


3 30
45 25 25
16 15 15

4 15
10 10 12 18
2 4 6 9

5 10
2 2 6 5 4
6 3 5 4 6


4 10
40 42 25 12
4 7 5 3
*/

实验测试结果及分析:
测试数据:
input.txt
在这里插入图片描述

通过运行程序得到:
output.txt
在这里插入图片描述

在该输出结果中,cv是当前节点的物品价值总和,cw是当前总重,bound是进入右子树后的最大值,bstv是进行到当前节点的全局最优解得值,形象的可以用如下图来表示该搜索过程:
在这里插入图片描述

最终我们得到最优解即选择物品1和物品3装入背包,得到的最优总价值为65。

复杂度分析:限界函数的时间复杂度为O(n),在最坏的情况下有2^n个右儿子结点需要计算上界,所以计算需要的最坏情况下的时间复杂度为O(n2 ^n)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1902014.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何选择一家适合自己的商城源码?

商城源码的选择取决于多个因素&#xff0c;包括商城的功能需求、稳定性、易用性、可定制性以及价格等。启山智软作为在市场上被广泛认可且表现优异的商城源码提供商&#xff0c;具有以下的特点和优势&#xff1a; 特点①&#xff1a;国内知名的B2B2C开源商城源码系统&#xff…

Go语言--工程管理、临时/永久设置GOPATH、main函数以及init函数

工作区 Go 代码必须放在工作区中。工作区其实就是一个对应于特定工程的目录&#xff0c;它应包含3个子目录:src 目录、pkg目录和bin 目录。 src 目录:用于以代码包的形式组织并保存 Go源码文件。(比如:.go.chs等)pkg 目录:用于存放经由 go install 命令构建安装后的代码包(包…

东芝TB6560AHQ/AFG步进电机驱动IC:解锁卓越的电机控制性能

作为一名工程师&#xff0c;一直在寻找可靠且高效的组件来应用于你的项目中。东芝的TB6560AHQ/AFG步进电机驱动IC能够提供精准且多功能的电机控制&#xff0c;完全符合现代应用的高要求&#xff0c;保证高性能和易用性。在这篇文章中&#xff0c;我们将探讨TB6560AHQ/AFG的主要…

陈志泊主编《数据库原理及应用教程第4版微课版》的实验题目参考答案实验2

实验目的 1&#xff0e;掌握在SQL Server中使用对象资源管理器和SQL命令创建数据库与修改数据库的方法。 2&#xff0e;掌握在SQL Server中使用对象资源管理器或者SQL命令创建数据表和修改数据表的方 法&#xff08;以SQL命令为重点&#xff09;。 实验设备 操作系统:Win11…

DEPTHAI 2.27.0 发布!

小伙伴们大家好&#xff0c;我们发布了DepthAI 2.27.0版本&#xff0c;本次对DepthAI库有了一些小更新&#xff0c;以下是更新内容。 功能 设置DEPTHAI_ENABLE_FEEDBACK_CRASHDUMP时自动故障转储收集&#xff1b; 漏洞修补 修复深度超出ImageAlign节点时生成PointCloud的问…

Matplotlib Artist 1 概览

Matplotlib API中有三层 matplotlib.backend_bases.FigureCanvas&#xff1a;绘制区域matplotlib.backend_bases.Renderer&#xff1a;控制如何在FigureCanvas上绘制matplotlib.artist.Artist&#xff1a;控制render如何进行绘制 开发者95%的时间都是在使用Artist。Artist有两…

Java开源ERP系统Axelor汉化方法初探

Axelor简介 汉化过程介绍 定义语言和本地化 导出多语言记录 导入翻译 验证翻译 调整翻译 Axelor简介 2024年6月份Axelor ERP发布了8.1版本&#xff0c;适配JDK11及PostgreSQL12及以上版本&#xff08;7及以前版本适配JDK8及PostgreSQL10&#xff09;数据库。v8版本较之前…

景区气象站:守护旅行安全的智能向导

在繁忙的现代社会&#xff0c;人们越来越渴望逃离城市的喧嚣&#xff0c;寻找一处宁静的自然之地放松身心。景区&#xff0c;作为大自然与人类文明交织的瑰宝&#xff0c;吸引了无数游客前来探访。然而&#xff0c;多变的天气往往给游客的旅行带来不确定性。 景区气象站&#x…

Python + OpenCV 开启图片、写入储存图片

这篇教学会介绍OpenCV 里imread()、imshow()、waitKey() 方法&#xff0c;透过这些方法&#xff0c;在电脑中使用不同的色彩模式开启图片并显示图片。 imread() 开启图片 使用imread() 方法&#xff0c;可以开启图片&#xff0c;imread() 有两个参数&#xff0c;第一个参数为档…

nginx的正向代理和反向代理以及tomcat

nginx的正向代理和反向代理&#xff1a; 正向代理以及缓存配置&#xff1a; 代理&#xff1a;客户端不再是直接访问服务端&#xff0c;通过代理服务器访问服务端。 正向代理&#xff1a;面向客户端&#xff0c;我们通过代理服务器的IP地址访问目标范围端。 服务端只知道代理…

10.x86游戏实战-汇编指令lea

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

云端AI大模型群体智慧后台架构思考

1 大模型的调研 1.1 主流的大模型 openai-chatgpt 阿里巴巴-通义千问 一个专门响应人类指令的大模型。我是效率助手&#xff0c;也是点子生成机&#xff0c;我服务于人类&#xff0c;致力于让生活更美好。 百度-文心一言&#xff08;千帆大模型&#xff09; 文心一言"…

Web漏洞扫描工具AppScan与AWVS测评及使用体验

AppScan和AWVS业界知名的Web漏洞扫描工具&#xff0c;你是否也好奇到底哪一个能力更胜一筹呢&#xff1f;接下来跟随博主一探究竟吧。 1. 方案概览 第一步&#xff1a;安装一个用于评测的Web漏洞靶场&#xff08;本文采用最知名和最广泛使用的靶场&#xff0c;即OWASP Benchma…

04.ffmpeg打印音视频媒体信息

目录 1、相关头文件 2、相关结构体 3、相关函数 4、函数详解 5、源码附上 1、相关头文件 #include <libavformat/avformat.h> 包含格式相关的函数和数据结构 #include <libavutil/avutil.h> 包含一些通用实用函数 2、相关结构体 AV…

Redis基础教程(九):redis有序集合

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

Zabbix触发器

目录 触发器基础概念 创建和管理触发器 示例 定义一个触发器 在 Zabbix 中&#xff0c;触发器&#xff08;Trigger&#xff09;用于定义在监控数据满足特定条件时触发警报或动作。触发器是实现监控告警和自动响应的核心组件之一。以下是关于 Zabbix 触发器的详细解释和用法…

刷代码随想录有感(127):动态规划——判断是否为子序列

题干&#xff1a; 代码&#xff1a; class Solution { public:bool isSubsequence(string s, string t) {vector<vector<int>>dp(s.size() 1, vector<int>(t.size() 1, 0));for(int i 1; i < s.size(); i){for(int j 1; j < t.size(); j){if(s[i …

警惕AI泡沫:巨额投资与回报失衡

尽管高科技巨头们在AI基础设施上投入巨资&#xff0c;但AI带来的收入增长尚未显现&#xff0c;揭示了生态系统末端用户价值的重大缺口。 红杉资本分析师David Cahn认为&#xff0c;AI企业需每年赚取约6000亿美元才能抵消其AI基础设施&#xff08;如数据中心&#xff09;的成本&…

02STM32环境搭建新建工程

STM32环境搭建&新建工程 软件安装&#xff1a;开发方式&新建工程步骤&架构 个人心得 软件安装&#xff1a; 安装Keil5 MDK 安装器件支持包 软件注册 安装STLINK驱动 安装USB转串口驱动 开发方式&新建工程步骤&架构 STM32开发方式&#xff1a; 1.寄存器 …

Python人形机踊跃跨栏举重投篮高维数动作算法模型

&#x1f3af;要点 &#x1f3af;运动功能&#xff1a; 1 m / s 1 m / s 1m/s上台阶、站立平衡、 1 m / s 1 m / s 1m/s行走、坐椅子、 5 m / s 5 m / s 5m/s跑步、 1 m / s 1 m / s 1m/s爬行、穿越森林、取物、穿越迷宫、 1 m / s 1 m / s 1m/s上滑梯、 5 m / s 5 m / s 5m/s…