算法修炼之筑基篇——筑基一层中期(解决01背包,完全背包,多重背包)

博主:命运之光​​​​​​

🦄专栏:算法修炼之练气篇​​​​​

🍓专栏:算法修炼之筑基篇

博主的其他文章:点击进入博主的主页​​​​​​

前言:学习了算法修炼之练气篇想必各位蒟蒻们的基础已经非常的扎实了,下来我们进阶到算法修炼之筑基篇的学习。筑基期和练气期难度可谓是天差地别,懂得都懂,题目难度相比起练气期的题目难度提升很多,所以要是各位蒟蒻小伙伴们看不懂筑基期的题目可以在练气期多积累积累,练气期的题目也会不断更新,大家一定要把基础打牢固了再来看筑基期的题目哈,这样子也可以提高大家的学习效率,一举两得,加油(●'◡'●)🎉🎉

目录

✨详解文字版(01背包,完全背包,多重背包)

🍓01背包问题

 🍓完全背包问题

🍓多重背包问题

✨01背包问题(经典)

🍓小明的背包1

🍓解法一:二维dp数组

🍓解法二:一维dp数组

✨完全背包问题(经典)

🍓小明的背包2

🍓一维dp数组

🍓一维dp数组关键步骤(记忆)

🍓01背包和完全背包两个对比(重要)

😭总结就一个公式(超级无敌重要)

🍓🍓01背包总结

🍓🍓完全背包总结

✨多重背包问题(经典)

🍓小明的背包3

🍓重要步骤


✨详解文字版(01背包,完全背包,多重背包)

光看文字我感觉,很难理解背包问题,关键还是要看看底下的经典例题,看完差不多就可以了,问题不好理解,大家加油哈(●'◡'●)

背包问题的理解和解法。这三种问题分别是:

  • 01背包问题:每种物品只能选择一次,求最大价值。
  • 完全背包问题:每种物品可以选择无限次,求最大价值。
  • 多重背包问题:每种物品有一个选择上限,求最大价值。

这三种问题都可以用动态规划的思想来解决,关键是找到状态转移方程。下面我就分别介绍一下这三种问题的思路和代码实现。(看不懂的话可以直接跳到例题,看例题我感觉就能直接理解,不用文字这么的啰嗦哈哈啊哈🤭)

🍓01背包问题

01背包问题是所有背包问题的基础,之后的问题都可以在此基础之上变化,所以一定要理解清楚。尤其是对待不同问题,找出状态转移方程是解题的关键。

假设有N件物品和一个容量为V的背包。第i件物品的体积是v[i],价值是w[i]。我们用f[i][j]表示前i件物品恰好放入一个容量为j的背包中的最大价值。那么我们可以得到如下的状态转移方程:

f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i])

这个方程的意思是,对于第i件物品,我们有两种选择:放或者不放。如果不放,那么前i件物品的最大价值就等于前i-1件物品的最大价值;如果放,那么前i件物品的最大价值就等于前i-1件物品在剩余空间j-v[i]中的最大价值加上第i件物品的价值。我们取这两种情况中较大的一个作为f[i][j]的值。

根据这个方程,我们可以用二维数组来存储f[i][j]的值,并从小到大遍历所有可能的状态。最终答案就是f[N][V]。

🍓下面是用C++实现的代码:

#include <iostream>
#include <algorithm>
using namespace std;const int N = 1001; // 物品数量和背包容量的上限
int f[N][N]; // 存储状态转移方程的结果
int v[N]; // 存储每件物品的体积
int w[N]; // 存储每件物品的价值int main() {
int n, V; // 物品数量和背包容量
cin >> n >> V;
for (int i = 1; i <= n; i++) {
cin >> v[i] >> w[i]; // 输入每件物品的体积和价值
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= V; j++) {
f[i][j] = f[i-1][j]; // 不放第i件物品
if (j >= v[i]) { // 如果能放得下第i件物品
f[i][j] = max(f[i][j], f[i-1][j-v[i]] + w[i]); // 取放和不放中较大的一个
}
}
}
cout << f[n][V] << endl; // 输出最大价值
return 0;
}


 🍓完全背包问题

完全背包问题与01背包问题非常相似,只是每种物品可以选择无限次而已。这样一来,我们的状态转移方程就有所变化:

f[i][j] = max(f[i-1][j], f[i][j-v[i]] + w[i])

这个方程的意思是,对于第i件物品,我们有两种选择:放或者不放。如果不放,那么前i件物品的最大价值就等于前i-1件物品的最大价值;如果放,那么前i件物品的最大价值就等于当前物品在剩余空间j-v[i]中的最大价值加上第i件物品的价值。注意这里和01背包问题的区别,因为可以放多次,所以是f[i][j-v[i]]而不是f[i-1][j-v[i]]。

根据这个方程,我们仍然可以用二维数组来存储f[i][j]的值,并从小到大遍历所有可能的状态。最终答案仍然是f[N][V]。

🍓下面是用C++实现的代码:

#include <iostream>
#include <algorithm>
using namespace std;const int N = 1001; // 物品数量和背包容量的上限
int f[N][N]; // 存储状态转移方程的结果
int v[N]; // 存储每件物品的体积
int w[N]; // 存储每件物品的价值int main() {
int n, V; // 物品数量和背包容量
cin >> n >> V;
for (int i = 1; i <= n; i++) {
cin >> v[i] >> w[i]; // 输入每件物品的体积和价值
}
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= V; j++) {
f[i][j] = f[i-1][j]; // 不放第i件物品
if (j >= v[i]) { // 如果能放得下第i件物品
f[i][j] = max(f[i][j], f[i][j-v[i]] + w[i]); // 取放和不放中较大的一个
}
}
}
cout << f[n][V] << endl; // 输出最大价值
return 0;
}

🍓多重背包问题

多重背包问题的描述是这样的:有n种物品和一个容量为m的背包,每种物品有一定的重量w[i]和价值v[i],还有数量限制num[i],即每种物品最多只能放num[i]个。问如何选择放入背包的物品,使得背包内物品的总价值最大,且不超过背包的容量。🤔

这个问题看起来很复杂,但其实我们可以用一些技巧来简化它。首先,我们可以把每种物品看成是若干个01背包问题中的物品,即把第i种物品分成num[i]个单独的物品,每个物品的重量和价值都是w[i]和v[i]。这样我们就把多重背包问题转化成了一个01背包问题。😎

然后,我们可以用一个二维数组dp[i][j]来表示从前i个物品中选择若干个放入容量为j的背包时能获得的最大价值。我们可以用一个循环来遍历所有的物品,对于每个物品,我们又用一个循环来遍历所有可能的背包容量,然后根据放或不放这个物品来更新dp[i][j]的值。具体来说,如果不放这个物品,那么dp[i][j]就等于dp[i-1][j],即前i-1个物品放入容量为j的背包时能获得的最大价值;如果放这个物品,那么dp[i][j]就等于dp[i-1][j-w[i]]+v[i],即前i-1个物品放入容量为j-w[i]的背包时能获得的最大价值加上当前物品的价值。我们要在这两种情况中取较大的那个作为dp[i][j]的值。👍

最后,我们可以输出dp[n][m]作为最终答案,即从n种物品中选择若干个放入容量为m的背包时能获得的最大价值。这就是多重背包问题的解法。🎉

01背包问题(经典)

🍓小明的背包1

🍓解法一:二维dp数组

#include<bits/stdc++.h>
using namespace std;
int dp[1005][1005],w[1005],v[1005];
int main()
{//经典的01背包问题需要记忆 int n,m;int i,j;cin>>n>>m;for(i=1;i<=n;i++){cin>>w[i]>>v[i];}for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(j<w[i]){dp[i][j]=dp[i-1][j];//记忆	}else{dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//记忆 }	}} 	 cout<<dp[n][m];	return 0;
}

🍓解法二:一维dp数组

#include<bits/stdc++.h>
using namespace std;
int dp[1005],w[1005],v[1005];
int main()
{//经典的01背包问题需要记忆 int n,m;int i,j;cin>>n>>m;for(i=1;i<=n;i++){cin>>w[i]>>v[i];}for(i=1;i<=n;i++){for(j=m;j>=1;j--)//--j和j--不影响,01背包是逆序 {if(j>w[i]){dp[j]=max(dp[j-1],dp[j-w[i]]+v[i]);}	}} 	 cout<<dp[m];	return 0;
}

🍓用这个dp[j]=max(dp[j],dp[j-w[i]]+v[i])

#include<bits/stdc++.h>
using namespace std;
int dp[1005],w[1005],v[1005];
int main()
{//经典的01背包问题需要记忆 int n,m;int i,j;cin>>n>>m;for(i=1;i<=n;i++){cin>>w[i]>>v[i];}for(i=1;i<=n;i++){for(j=m;j>=1;j--)//--j和j--不影响,01背包是逆序 {if(j>=w[i])dp[j]=max(dp[j],dp[j-w[i]]+v[i]);	}} 	 cout<<dp[m];	return 0;
}

下面是需要记忆知识点(01背包问题中的推到公式需要记忆)

🍓二维dp数组

	for(i=1;i<=n;i++){for(j=1;j<=m;j++){if(j<w[i]){dp[i][j]=dp[i-1][j];//记忆	}else{dp[i][j]=max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);//记忆 }	}}

🍓一维dp数组(重要)

for(i=1;i<=n;i++)
{for(j=m;j>=1;j--)//--j和j--不影响,01背包是逆序 {if(j>=w[i])dp[j]=max(dp[j],dp[j-w[i]]+v[i]);	}} 

完全背包问题(经典)

🍓小明的背包2

🍓一维dp数组

#include<bits/stdc++.h>
using namespace std;
int dp[1005],w[1005],v[1005];
int main()
{//经典的完全背包问题需要记忆 int n,m;int i,j;cin>>n>>m;for(i=1;i<=n;i++){cin>>w[i]>>v[i];}for(i=1;i<=n;i++){for(j=w[i];j<=m;j++){dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}} 	 cout<<dp[m];	return 0;
}

🍓一维dp数组关键步骤(记忆)

	for(i=1;i<=n;i++){for(j=w[i];j<=m;j++){dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}} 	 

🍓01背包和完全背包两个对比(重要)

😭总结就一个公式(超级无敌重要)

dp[j]=max(dp[j],dp[j-w[i]]+v[i]);

其中01背包是逆向推到,完全背包是正向推到

🍓🍓01背包总结

for(i=1;i<=n;i++)
{for(j=m;j>=1;j--)//--j和j--不影响,01背包是逆序 {if(j>=w[i])dp[j]=max(dp[j],dp[j-w[i]]+v[i]);	}} 

🍓🍓完全背包总结

for(i=1;i<=n;i++)
{for(j=w[i];j<=m;j++)//重要点,j=w[i];j<=m;j++{dp[j]=max(dp[j],dp[j-w[i]]+v[i]);}} 

✨多重背包问题(经典)

理解了上面的01背包问题和多重背包问题就很好理解多重背包问题了

🍓小明的背包3

#include<bits/stdc++.h>
using namespace std;
int dp[205],w[205],vi[205],s[205];
int main()
{//经典的多重背包问题 int n,v;int i,j;cin>>n>>v;for(i=1;i<=n;i++){cin>>w[i]>>vi[i]>>s[i];}for(i=1;i<=n;i++){for(j=v;j>=1;j--){for(int k=0;k<=s[i]&&j>=k*w[i];++k){//从01背包的状态转移方程式,去增加i个物品拿k个循环 dp[j]=max(dp[j],dp[j-k*w[i]]+k*vi[i]);}}}cout<<dp[v]; return 0;
}

🍓重要步骤

🍓就是在01背包的基础上加上K循环的约束条件dp[j]=max(dp[j],dp[j-k*w[i]]+k*vi[i])

for(i=1;i<=n;i++){for(j=v;j>=1;j--)//01背包的基础上他这个也是逆向的{for(int k=0;k<=s[i]&&j>=k*w[i];++k){//从01背包的状态转移方程式,去增加i个物品拿k个循环 dp[j]=max(dp[j],dp[j-k*w[i]]+k*vi[i]);}}}

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

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

相关文章

客户回访|国产MCU测试解决方案 助力中国“芯”智造

半导体技术持续更新迭代&#xff0c;MCU也在与时俱进&#xff0c;为了更好地迎接市场未来趋势&#xff0c;国产MCU厂商积极布局各系列MCU产品线&#xff0c;开始逐渐在特定细分领域实现突破。随着应用场景的进化升级&#xff0c;MCU 中包含越来越多的功能模块&#xff0c;相应地…

UEFI——保姆级教程的HelloWold Application

HelloWorld Application 前言为什么是HelloWorldApplication又是什么代码部分test_application.c文件test_application.inf编译运行 总结 前言 毕业之后工作开始接触UEFI&#xff0c;现在为止也不过短短的四个月&#xff0c;UEFI开发涉猎面广&#xff0c;知识体系庞杂&#xf…

云南省工信厅洪正华一行莅临红谷滩区·高通中国·影创联合创新中心考察调研

9月9日上午&#xff0c;国内首个XR(扩展现实)产业联合创新中心自开业后&#xff0c;迎来首批参观领导。云南省工信厅党组书记、厅长洪正华&#xff0c;云南省工信厅党组成员、副厅长袁国书&#xff0c;云南省工信厅综合处处长薛贵辉&#xff0c;云南省工信厅发展规划处处长王兵…

UEFI Protocol

一、概述 二、Protocol的定义 1、Protocol是服务器端和客户端之间的一种约定&#xff0c;在软件编程上称为接口&#xff0c;服务器端和客户端通过这个约定信息的互通。 2、服务器端和客户端在UEFI中都是可执行的二进制文件。 3、为了实现这些二进制文件之间的互通&#xff0c;…

UEFI原理与编程(二):UEFI工程模块文件-标准应用程序工程模块

UEFI 工程模块文件-标准应用程序工程模块 前言 在EDK2环境下编程之前&#xff0c;先介绍EDK2的两个概念模块(Module)和包(Package).   “包”是一组模块及平台描述文件(.dsc文件)、包声明文件(.dec文件)则、组成的集合&#xff0c;多在以*pkg命名的文件夹中&#xff0c;一般…

如何理解UEFI的事件机制(三)——时钟中断

一&#xff0c;时钟中断概述 UEFI 中的EVENT是使用时钟中断来驱动的。 在时钟中断处理函数中&#xff0c;它会检查系统中的定时器事件并处理到期的定时器事件&#xff0c;并在合适的时机调度事件的Notify函数&#xff0c;是事件的实现基础。时钟中断在DXE的主函数DxeMain中初始…

UEFI原理与编程(七):包及.dsc、.dec、.fdf文件

包及.dsc、.dec、.fdf文件 前言 前面的文章中比较详细介绍了UEFI工程文件即.inf。UEFI的包中一般都会包含一个.dsc文件和一个dec文件。在包生成固件Image、Option Rom Image&#xff0c;这个包还要包含.fdf文件。.fdf用于生成固件Image、Option Rom Image或可以启动Image。 b…

UEFI学习——在qemu上读取设备PCI信息

1.编写读取设备PCI信息的Application 代码参考罗斌大佬&#xff0c;博客地址&#xff1a;UEFI开发探索13 – 访问PCI/PCI-E设备1 感谢罗斌大佬的贡献&#xff0c;让我在学习UEFI的道路上站在了巨人的肩膀上。 代码&#xff1a; #include <Uefi.h> #include <L…

UEFI开发与调试---ImageHandle和ControllerHandle

##1.ImageHandle 每个uefi module都是一个image&#xff0c;而每个image对应都有一个ImageHandle&#xff0c;其实ImageHandle的类型就是EFI_HANDLE typedef VOID *EFI_HANDLE;因此实际上每个ImageHandle是一个void*指针&#xff0c;那么也就是说任何结…

UEFI的诞生与优势

UEFI的诞生 随着CPU及其他硬件设备的技术革新&#xff0c;CPU和操作系统已支持到64位&#xff0c;而BIOS却还停留在16位&#xff1b;主板上更加丰富多样的扩展设备&#xff0c;也让BIOS愈加无能为力&#xff0c;使得硬件无法完成初始化。现实表明&#xff0c;BIOS已经无法满足…

UEFI shell - 标准应用程序的编译和加载过程

一、标准应用工程编译 首先了解下,应用程序是怎么被编译成.efi文件: UefiMain.c首先被编译成目标文件UefiMain.obj连接器将目标文件UefiMain.obj和其他库连接成UefiMain.dllGenFw工具将UefiMain.dll转换成UefiMain.efi 说明:连接器在生成UefiMain.dll时使用了/dll/entry:_Mo…

UEFI开发,记录第一场胜利——调用一个自己编写的protocol

本文参考BIOS/UEFI基础——Protocol介绍 大四第一个签三方的工作&#xff0c;BIOS的开发&#xff0c;第一次接触这个领域&#xff0c;在实习之前很好奇&#xff0c;也很有兴趣&#xff0c;但是学习BIOS在一开始注定要碰多次碰壁&#xff0c;实习第三周第二天&#xff0c;终于写…

linux系统nohob安装,Linux启动详解1

一、固件运行 本部分主要参考 戴正华 著《UEFI原理与编程》 CPU在加电后会进入16位实模式状态运行&#xff0c;同时CPU的逻辑电路设计为加电瞬间将CS的值设置为 0xF000、IP的值置为0xFFF0&#xff0c;这样CS&#xff1a;IP就指向0xFFFF0这个地址位置。然后开始执行固件 固件的执…

《UEFI原理与编程》读书笔记

《UEFI原理与编程》读书笔记 读书笔记仅摘取对个人有用的部分&#xff0c;详细内容请阅读原著 目前更新至第七章&#xff0c;因个人仅需要引导程序&#xff0c;故其余部分未介绍&#xff0c;详细内容请阅读原著 书及其资料均已放置在本人资源中&#xff0c;如需下载请移步资源模…

UEFI简介

前言 大多数人接触UEFI都是在PC的应用场景上&#xff0c;有在PC上安装过多操作系统的经历的同学&#xff0c;通常会进入UEFI界面设置操作系统引导顺序、CPU虚拟化等设置。UEFI诞生之初也确实是作为BIOS的替代者&#xff0c;主要应用在PC电脑上。随着手机/平板等移动设备的发展&…

10月书讯(上) | 小长假我读这些新书

华章科技提前祝大家国庆快乐 7天小长假&#xff0c;正是读书好时节 又到上新季&#xff0c;读书与休假更配哦 10月书讯&#xff08;上&#xff09;请查收 快来看看哪本书最属你心意 参与文末赠书活动&#xff0c;好书就要抢先读 — 新书速览 — 1、《计算机系统解密&#xff1a…

UEFI启动流程浅析

BIOS启动流程 SEC&#xff08;Security Phase&#xff0c;安全阶段&#xff09;阶段 SEC阶段是平台初始话的第一个阶段&#xff0c;计算机系统加电后首先进入这个阶段。 CPU上电之后&#xff0c;首先会进行硬件初始化&#xff08;hard reset&#xff09; 其次会进行可选的自检…

UEFI规范实现EDKII项目学习笔记绪论[0]

UEFI规范实现EDKII项目学习笔记绪论[0] 2015-07-10 北京海淀区 张俊浩 这段时间在学习UEFI( Unified Extensible Firmware Interface,统一的可扩展固件接口)&#xff0c;熟悉EDKII&#xff08;EFI Developer KitII&#xff0c;EFI开发工具包&#xff09;项目&#xff0c;…

3.UEFI-edk2 增加中文显示

UEFI-edk2源码中默认只有英文和法文的字库&#xff0c;在UI界面上或者shell终端打印中文字符串&#xff0c;则无法显示。例如&#xff0c;上一篇博客中的TestoneApp.cpp中&#xff0c;增加一行带中文字符串的打印&#xff1a; Print(L"Hello, world!\r\n");Print(L&…

UEFI原理与编程(一)

第一章 UEFI概述(Unified Extensible Firmware Interface 统一的可扩展固件接口) 常见缩写及描述&#xff1a; 缩略词全名描述UEFIUnified Extensible Firmware Interface统一的可扩展固件接口BSBoot Services启动服务RTRuntime Service运行时服务BIOSBasic Input Output Sys…