C++算法初级11——01背包问题(动态规划2)

news2024/12/27 3:11:16

C++算法初级11——01背包问题(动态规划2)

文章目录

  • C++算法初级11——01背包问题(动态规划2)
    • 问题引入
    • 0-1背包问题分析
    • 0-1背包问题的形式化分析
    • 优化

问题引入

辰辰采药

辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?

辰辰采药在算法中属于一种很经典的0-1背包问题。更一般的,这种问题可以转化为:

给定n个物品,每个物体有个体积v i和一个价值p i
​现有一个容量为V的背包,请问如何选择物品装入背包,使得获得的总价值最大?

0-1背包问题分析

0-1背包问题描述:给定n个物品,每个物体有个体积v i和一个价值p i
​现有一个容量为V的背包,请问如何选择物品装入背包,使得获得的总价值最大?

基本思路
考虑到现在我们能做的决策,只有对于每个物品的“选”与“不选”。所以,这个问题就是

  • 以“将每一个物品依次放入背包”划分不同阶段
  • 而对于每个物品的“选与不选”就是不同决策

考虑到所有的放置前i个物品的方案数可以分为两类:

  • 一个是放第i个物品,
  • 一个是不放第i个物品

所以下面我们分这两种情况来讨论。因为在决策的过程中,变化的是当前所在的阶段,以及容量的剩余大小。
所以,我们维护一个二维状态f[i,j], 来表示前i个物品,放到体积为j的背包里,可以得到的最大价值。

首先,考虑容量为任意值j时,将前i个物品放入背包的情况。

在这里插入图片描述
所以,状态转移方程为 f [ i ] [ j ] = m a x { f [ i − 1 , j ] , p [ i ] + f [ i − 1 ] [ j − v [ i ] } f[i][j]=max\{f[i-1,j],p[i]+f[i-1][j-v[i]\} f[i][j]=max{f[i1,j],p[i]+f[i1][jv[i]}

0-1背包问题的形式化分析

使用动态规划解决问题,需要明确状态设计、转移方程、初始状态和转移方向四个方面。

那现在,让我们来明确一下该0-1背包问题中的动态规划四要素:

  1. 状态:
    用f[i][j]表示前i个物品,放在空间为j的背包里,能获得的最大收益。
  2. 转移方程:
    因为每一个阶段有至多两种选择,所以需要分别计算两种选择的收益后取较大值。
f[i][j] = f[i - 1][j]					// j < v[i],表示装不下第i个物品
f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + p[i]);	// otherwise
  1. 初始状态:
    在一个物品都没放的时候,无论背包大小多少,总价值都是0,即
f[0][j] = 0 // 0 <= j <= V
  1. 转移方向:
    观察转移方程,发现要想保证等式右边的状态一定比左边的状态先算出来,只需要保证i从小到大计算即可。
    最终该问题的答案就是f[n,V]。这样,0-1背包问题就可以使用动态规划来解决~

代码

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

# define N 1002
int n=3;
int V = 70;
vector<int> v = {0,71,69,1};//体积
vector<int> p ={0,100,1,2};//价值
// 第0位,置为0,不参与计算,便于与后面的下标进行统一
int f[N][N];

int main()
{
    for(int j=0;j<=V;j++)
        f[0][j]=0;
    for(int i=1;i<=n;i++)
    {
        if(v[i]>V)
            continue;
        for(int j=0;j<=V;j++)
        {
            if(j<v[i])
                f[i][j]=f[i-1][j];
            else
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+p[i]);
        }
    }
    cout<<f[n][V]<<endl;
}

动态规划的主要工作:就是算出不同状态下的结果,然后用相应维数的数组保存。

所以,整个动态规划的过程就是一个”填表“的过程。
在这里插入图片描述

优化

在这里插入图片描述
滚动数组优化
因为整个动态规划的过程就是一个填表的过程,如下图:
在这里插入图片描述
而在本题中,填表的顺序就是:填完上一行,然后填下一行。而且我们发现,下一行的状态,只会用到上一行的状态来转移。所以,当我们在计算第i行时,其实前i−2行的状态就都没必要保留了。所以,我们可以用一种类似于”踩石头过河“的思想。

试想如果我们有一些石头,想利用这些石头过河。

如果石头的数量很多,那么最方便的方法就是用这些石头铺一道石头路,这样我们就可以顺利过河。这就相当于我们可以开很充足的数组,然后把计算的每个阶段都存在数组里。

但如果我们只有两块石头,就过不了河了吗?不是的。我们可以用下图的方法一边走一边挪动石头,这样也可以顺利过河。
在这里插入图片描述
在空间优化的方法中,有一种很常见就是利用过河的思想。这种方法叫做滚动数组。在整个算法过程中,我们只用2×V的数组f[2][V]来记录状态。其中,所有奇数行的状态填入f[1][j]中,所有偶数行的状态填入f[0][j]中,如下图
在这里插入图片描述

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

# define N 1002
int n=3;
int V = 70;
vector<int> v = {0,71,69,1};//体积
vector<int> p ={0,100,1,2};//价值
// 第0位,置为0,不参与计算,便于与后面的下标进行统一
int f[2][N];//f[i][j]表示前i个物品,体积为j的最大价值

int main()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=V;j++)
        {
            if(j<v[i])
                f[i&1][j] = f[(i-1)&1][j];
            else
                f[i&1][j] = max(f[(i-1)&1][j],f[(i-1)&1][j-v[i]]+p[i]);
        }
    }
    cout<<f[n&1][V]<<endl;
    return 0;
}

算法优化2 —— 优化到一维数组
那么我们可不可以再进一步优化空间,使得只用一个一维数组就能解决整个问题了呢?

想到之前“踩石头过河”的类比,我们可能会觉得不太可能。但是如果我们进一步分析整个表的填写,如下图:
在这里插入图片描述
会发现下一行的某个状态,正好是由它上面的元素,以及左上方的某个元素转移而来。所以我们需要保证当计算黄色状态时上面两个绿色状态没有被覆盖掉。所以,当我们计算第i行时,完全可以将j从大到小枚举,这样在计算状态f(i,j)之前,数组f[j]中存储的是状态f[i−1,j],更新完以后,
f[j]中存的状态就是f[i,j]了。如下图:
在这里插入图片描述

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

# define N 1002
int n=3;
int V = 70;
vector<int> v = {0,71,69,1};//体积
vector<int> p ={0,100,1,2};//价值
// 第0位,置为0,不参与计算,便于与后面的下标进行统一
int f[N];

int main()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=V;j>=v[i];j--)
        {
            f[j] = max(f[j],f[j-v[i]]+p[i]);
        }
    }
    cout<<f[V]<<endl;
    return 0;
}

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

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

相关文章

Ubuntu开机自启动一些东西

有三种方式做开机自启动 目录 1.免除sudo密码 2.Startup 2.desktop 3.service 1.免除sudo密码 做完这一步你的所有sudo命令都不会再让你输密码了 如果你的开机自启动的东西需要sudo&#xff0c;那么这一步就是必须的&#xff0c;如果不需要sudo&#xff0c;那么这一步可…

Linux安装kubectl

前言 以下所有命令基于CentOS7.9系统&#xff0c;官方参考文档&#xff1a;> 文章最后附有一键安装的脚本&#xff0c;可以直接运行脚本进行安装 下载安装文件 1. 下载最新发行版 curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/st…

C嘎嘎~~[类 中篇]

类 中篇 6.类的实例化7.类对象模型8.this指针8.1this指针是什么8.2this指针的特性 6.类的实例化 什么叫类的 实例化?? 首先, 我们应该关注这个"实" — 实际存在的, 它的反义词是 “虚” — 不存在的. > 类中的成员变量是虚的(相当于声明), 在类外面创建的对象是…

《程序员面试金典(第6版)》面试题 16.06. 最小差(双指针,pair数据结构)

题目描述 给定两个整数数组a和b&#xff0c;计算具有最小差绝对值的一对数值&#xff08;每个数组中取一个值&#xff09;&#xff0c;并返回该对数值的差 示例&#xff1a; 输入&#xff1a;{1, 3, 15, 11, 2}, {23, 127, 235, 19, 8}输出&#xff1a;3&#xff0c;即数值对(…

Power BI动态日期轴方法总结

趋势&#xff0c;应该是我们做可视化时最熟悉的一个词了&#xff0c;看趋势自然离不开日期&#xff0c;年度趋势&#xff0c;月趋势&#xff0c;周趋势等等。Power BI中我们可以借助于计算表&#xff0c;计算组&#xff0c;字段参数来实现动态实时轴的效果。 计算表实现动态日…

Node.js--基础

一、Node.js是什么 Node.js is a JavaScript runtime built on Chrome’s V8 JavaScript engine. 1、特性 Node.js 可以解析JS代码&#xff08;没有浏览器安全级别的限制&#xff09;提供很多系统级别的API&#xff0c;如&#xff1a; 文件的读写 (File System)进程的管理 …

每日一个小技巧:1分钟告诉你文字转图片的方法有哪些

在数字时代&#xff0c;信息传递快速便捷&#xff0c;但文字在传递中却显得单调乏味&#xff0c;难以吸引人们的眼球。为了解决这个问题&#xff0c;越来越多的人开始寻找方法将文字转化为图片。文字转图片不仅能够让文字更具视觉冲击力&#xff0c;还能够在社交媒体、广告宣传…

Nginx常见应用场景

文章目录 场景一&#xff1a;代理静态文件场景二&#xff1a;代理服务器 本教程讲述 Nginx 的常见应用场景。内容接上文&#xff1a;Nginx基本配置。 前提&#xff1a;假设我们已经安装好了 Nginx&#xff0c;并启动成功。 场景一&#xff1a;代理静态文件 Nginx 一个常用的功…

Hilt 和协程助力启动框架搭建:解决代码混乱和初始化策略问题

关于Hilt的使用&#xff0c;目前已经比较普及了&#xff0c;想必大家已经知道。今天说的是一个如何利用Hilt来做一个启动框架的故事。 是否经历过大型项目的启动优化&#xff0c;一遍过去无任何效果&#xff0c;第二遍过去好几处报错&#xff0c;第三遍过去启动不了&#xff0…

开放耳机有什么优缺点,列举出几款口碑不错的开放式耳机

开放式耳机是通过骨头振动传递声音&#xff0c;而不是通过耳道或鼓膜&#xff0c;因此它具有许多优势&#xff0c;比如可以在运动过程中保持对环境的感知&#xff0c;并避免对听力造成伤害。随着科技的进步和用户需求的增加&#xff0c;开放式耳机也在不断更新。目前市面上的开…

springboot+nodejs+vue众筹项目管理系统平台系统

筹资人&#xff08;企业&#xff09;&#xff1a; 1&#xff0c;可以后台注册并登录&#xff0c;发布项目情况&#xff0c;众筹项目需要管理员审核通过后才能展现在前台&#xff0c;没有审核或者审核不通过不会在前台展示&#xff1b; 众筹项目包括项目名称&#xff0c;项目分类…

盲目自学网络安全只会成为脚本小子?

前言&#xff1a;我们来看看怎么学才不会成为脚本小子 一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09; 了解网络安全相关法律法规 熟悉基本概念&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#xff09;。 通过关键字&…

springboot整合flowable的简单使用

内容来自网络整理&#xff0c;文章最下有引用地址&#xff0c;可跳转至相关资源页面。若有侵权请联系删除 环境&#xff1a; mysql5.7.2 springboot 2.3.9.RELEASE flowable 6.7.2 采坑&#xff1a; 1.当前flowable sql需要与引用的pom依赖一致&#xff0c;否则会报library…

管理后台项目-07-菜单管理和动态展示菜单和按钮

目录 1-菜单管理 1.1-菜单管理列表 1.2-添加|修改功能 1.3-删除菜单 2-动态菜单按钮展示 2.1-路由文件的整理 2.2-动态展示不同的路由 1-菜单管理 当用户点击菜单管理的时候&#xff0c;会展示当前所有菜单&#xff0c;树型结构展示...并且可以对菜单进行新增编辑删除操…

倾斜摄影超大场景的三维模型在网络发布应用遇到常见的问题浅析

倾斜摄影超大场景的三维模型在网络发布应用遇到常见的问题浅析 倾斜摄影超大场景的三维模型在网络发布应用时&#xff0c;常见的问题包括&#xff1a; 1、加载速度慢。由于数据量巨大&#xff0c;网络发布时需要将数据文件分割成多个小文件进行加载&#xff0c;这可能会导致页…

Sonatype Nexus兼容apk格式仓库

Sonatype Nexus兼容apk格式仓库 sonatype/nexus3 当前最新版本&#xff1a;sonatype/nexus3:3.52.0 查看nexus支持的仓库格式 创建一个nexus 容器&#xff1a; docker run -d -p 8081:8081 --name nexus sonatype/nexus3:3.52.0查看启动日志&#xff1a; docker logs nexu…

HTML5画布(图像)

案例1&#xff1a; <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"><title></title><script>window.onloadfunction(){var cdocument.getElementById("myCanvas");var cxt c.getConte…

Vue3 手把手按需引入 Echarts

背景&#xff1a;新项目采用 Vue3 作为前端项目框架&#xff0c;避免不了要使用 echarts&#xff0c;但是在使用的时候&#xff0c;出现了与 Vue2 使用不一样的地方&#xff0c;所以特别记下来&#xff0c;希望给到有需要的同学一些帮助。 下载Echarts依赖 # 自己使用的yarn y…

《Odoo开发者模式必知必会》—— 缘起

Odoo作为业界优秀的开源商务软件&#xff0c;在全球范围内拥有广泛的使用者。在领英国际&#xff0c;可以搜索到全球很多国家都有大量odoo人才需求的招聘信息。在国内&#xff0c;虽然已经有为数不少的企业&#xff0c;他们或者已经使用odoo&#xff0c;或者正在了解odoo&#…

支付宝异步通知说明

如何设置异步通知地址 不同接口接收异步通知设置方式不同&#xff0c;可查看 哪些接口支持触发异步。 设置 notify_url 接收异步 对于支付产生的交易&#xff0c;支付宝会根据原始支付 API 中传入的异步通知地址 notify_url&#xff0c;通过 POST 请求的形式将支付结果作为参…