算法日记25:01背包(DFS->记忆化搜索->倒叙DP->顺序DP->空间优化)

在这里插入图片描述

  • 对于01背包这类DP入门的问题,新手应该是去了解如何一步步得出所谓的状态转移方程,而不是直接去看答案所给予的方程
  • 过程应该为:DFS->记忆化搜索->倒序递推->循序递推->二维->一维

一、DFS暴力搜索 O ( 2 n ) O(2^n) O(2n)

1.1:思路讲解:

  • 无需多说,最暴力的做法
  • 最经典的指数级枚举(每个物品选/不选)
    在这里插入图片描述

1.2:代码解析:

#include <bits/stdc++.h>
using namespace std;const int N = 1007;
int vi[N],wi[N];
int n, v;int dfs(int x, int Spv)//从第x件物品开始,当前剩余容量为Spv
{if (x > n) return 0;if (Spv < vi[x])    //容量不够拿不了{return dfs(x + 1, Spv);}else //表明容量够{return max(dfs(x + 1, Spv), dfs(x + 1, Spv - vi[x]) + wi[x]); //不拿/拿}
}void solve()
{cin >> n >> v;for (int i = 1; i <= n; i++){cin >> vi[i]>>wi[i];}int res=dfs(1, v);//从第一件物品开始,当前剩余容量为vcout << res <<'\n';
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ =1; //cin >> _;while (_--) solve();system("pause");return 0;
}

对于暴力来说,只要样例大于20,一般都会超时

二、记忆化搜索: O ( N ∗ V ) O(N*V) O(NV)

2.1:思路讲解:

  • 1、相比所谓的暴力搜索,优化了大量的时间复杂度(指数级->线性级)
  • 2、所谓记忆化搜索,就是把DFS计算过的数据不再重复计算(用一个mem数组存储状态)
    PS :记忆化数组的数据个数一般和DFS函数的参数一致

2.2:代码解析:

#include <bits/stdc++.h>
using namespace std;const int N = 1007;
int vi[N],wi[N];
int n, v;
int mem[N][N];
int f[N];int dfs(int x, int SpV)//从第x件物品开始,当前剩余容量为Spv
{if(mem[x][SpV]) return mem[x][SpV];if(x>n) return 0;   //表示已经拿完了if(SpV>=vi[x]) //能够拿这个物品{//此时,考虑 不拿/拿return  mem[x][SpV]=max(dfs(x+1,SpV),dfs(x+1,SpV-vi[x])+wi[x]);}        else //不能拿{return mem[x][SpV]=dfs(x+1,SpV);}
}void solve()
{cin >> n >> v;for (int i = 1; i <= n; i++){cin >> vi[i]>>wi[i];}cout<<dfs(1,v)<<'\n';}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ =1; //cin >> _;while (_--) solve();system("pause");return 0;
}

三、倒序动态规划 O ( N ∗ V ) O(N*V) O(NV)

3.1:思路讲解

  • 1、典型的空间换时间的做法,相比于搜索,节约了大量的时间复杂度
  • 2、动态规划的for循环变量的遍历 应该与DFS边界的限制的参数相同(例如:本题目中,边界与物品数量X、剩余的体积SPV有关)所以循环为n/v作为参数
  • 3、因为在递归中,只有才是产生答案的过程,所以可以从边界直接开始推出答案
    在这里插入图片描述

3.2:代码解析

#include <bits/stdc++.h>
using namespace std;const int N = 1007;
int vi[N],wi[N];
int n, v;
int mem[N][N];
int f[N][N];// int dfs(int x, int Spv)//从第x件物品开始,当前剩余容量为Spv
// {
//     if (mem[x][Spv]) return mem[x][Spv];//     int sum = 0;
//     if (x > n) sum= 0;//     else if (Spv < vi[x])    //容量不够拿不了
//     {
//         sum=dfs(x + 1, Spv);
//     }
//     else //表明容量够
//     {
//         sum = max(dfs(x + 1, Spv), dfs(x + 1, Spv - vi[x]) + wi[x]); //不拿/拿
//     }
//     mem[x][Spv] = sum;
//     return mem[x][Spv];
// }void solve()
{cin >> n >> v;for (int i = 1; i <= n; i++){cin >> vi[i]>>wi[i];}for (int i = 1; i <= n; i++){for (int j = 0; j <= v; j++){if (j < vi[i])  //当前剩余容量<物品容量{f[i][j] = f[i - 1][j];} else   //表明容量够{f[i][j] = max(f[i - 1][j], f[i - 1][j - vi[i]] + wi[i]);  //拿/不拿}}}cout << f[n][v] <<'\n';
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ =1; //cin >> _;while (_--) solve();system("pause");return 0;
}

四、顺序动态规划:

4.1:思路讲解:

  • 1、倒序遍历物品总是怪怪的,那么可不可以正序枚举呢,当然是可以的。
  • 2、此时,状态转移方程与DFS搜索的方程保持一致
    PS:正序枚举相当于以n->1开始递归,此时边界刚刚好是为1,所以循环从1开始

4.2:代码解析:

#include <bits/stdc++.h>
using namespace std;const int N = 1007;
int vi[N],wi[N];
int n, v;
int mem[N][N];
int f[N][N];// int dfs(int x, int Spv)//从第x件物品开始,当前剩余容量为Spv
// {
//     if (mem[x][Spv]) return mem[x][Spv];//     int sum = 0;
//     if (x > n) sum= 0;//     else if (Spv < vi[x])    //容量不够拿不了
//     {
//         sum=dfs(x + 1, Spv);
//     }
//     else //表明容量够
//     {
//         sum = max(dfs(x + 1, Spv), dfs(x + 1, Spv - vi[x]) + wi[x]); //不拿/拿
//     }
//     mem[x][Spv] = sum;
//     return mem[x][Spv];
// }void solve()
{cin >> n >> v;for (int i = 1; i <= n; i++){cin >> vi[i]>>wi[i];}for (int i = 1; i <= n; i++){for (int j = 0; j <= v; j++){if (j < vi[i])  //当前剩余容量<物品容量{f[i][j] = f[i - 1][j];} else   //表明容量够{f[i][j] = max(f[i - 1][j], f[i - 1][j - vi[i]] + wi[i]);  //拿/不拿}}}cout << f[n][v] <<'\n';
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ =1; //cin >> _;while (_--) solve();system("pause");return 0;
}

五、二维 -> 一维:

5.1:思路讲解(为什么要逆序枚举体积??)

1、注意01背包的二维的遍历应该的逆序,为什么呢?

  • 看下图,如果是正序的话,那么结果就会从 i i i 的状态 − > -> > i − 1 i-1 i1的状态
  • 而物品 i 的状态此时表明已经遍历过了,也就是已经选过了,那么此时就变成了从这个物品已经选过的状态—>下一个状态
  • 那么此时,这个物品就重复选了!!!,这就变成了完全背包!!!(也就是物品的数量无限)
    在这里插入图片描述

2、但是如果是逆序的话,那么就会从 i − 1 — > i − 1 的状态 i-1—>i-1的状态 i1—>i1的状态

  • 表明此时的状态为,没选过的状态—>下一个状态
  • 那么此时,就会使得状态转移为:还没选过这个物品的情况->更新*还没选过这个物品的情况
  • 这就符合01背包的背景!!!故01背包应该逆序
    在这里插入图片描述

5.2:代码解析:

#include <bits/stdc++.h>
using namespace std;const int N = 1007;
int vi[N],wi[N];
int n, v;
int mem[N][N];
int f[N];// int dfs(int x, int Spv)//从第x件物品开始,当前剩余容量为Spv
// {
//     if (mem[x][Spv]) return mem[x][Spv];//     int sum = 0;
//     if (x > n) sum= 0;//     else if (Spv < vi[x])    //容量不够拿不了
//     {
//         sum=dfs(x + 1, Spv);
//     }
//     else //表明容量够
//     {
//         sum = max(dfs(x + 1, Spv), dfs(x + 1, Spv - vi[x]) + wi[x]); //不拿/拿
//     }
//     mem[x][Spv] = sum;
//     return mem[x][Spv];
// }void solve()
{cin >> n >> v;for (int i = 1; i <= n; i++){cin >> vi[i]>>wi[i];}for (int i = 1; i <= n; i++){for (int j = v; j >= 0; j--){if(j>=vi[i]) f[j] = max(f[j], f[j - vi[i]] + wi[i]);  //拿/不拿}}cout << f[v] <<'\n';
}int main()
{ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);int _ =1; //cin >> _;while (_--) solve();system("pause");return 0;
}

总结:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring AutoWired与Resource区别?

大家好&#xff0c;我是锋哥。今天分享关于【Spring AutoWired与Resource区别?】面试题。希望对大家有帮助&#xff1b; Spring AutoWired与Resource区别? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Spring 中&#xff0c;Autowired 和 Resource 都是用于…

【知识】深度学习中,应该先zero_grad还是先backward?

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 抛出问题 各大GPT的回答 ChatGPT-4o ChatGPT-o3-mini-high Kimi-长思考 Deepseek-R1 Grok3 Pytorch官方教程中 抛出问题 以下哪种方式是…

Python----数据结构(哈希表:哈希表组成,哈希冲突)

一、哈希表 哈希表(Hash table)是一种常用、重要、高效的数据结构。 哈希表通过哈希函数,可以快速地将键(Key)映射到值(Value)。从而允许在近常数时间内对键关联的值进行插入、删除和查找操作。 哈希表的主要思想是通过哈希函数将键转换为索引&#xff0c;将索引映射到数组中…

使用excel中的VBA合并多个excel文件

需求是这样的&#xff1a; 在Windows下&#xff0c;用excel文件让多个小组填写了统计信息&#xff0c;现在我需要把收集的多个文件汇总到一个文件中&#xff0c;前三行为标题可以忽略&#xff0c;第四行为收集信息的列名&#xff0c;处理每一行数据的时候&#xff0c;发现某一行…

功能全面的手机壁纸应用,种类齐全、众多高清壁纸

软件介绍 应用亮点&#xff1a;今天给大家分享一款超神奇的手机应用 —— 奇幻壁纸。它作为手机动态壁纸软件&#xff0c;功能超全面&#xff0c;操作还便捷&#xff0c;极具创意&#xff0c;能瞬间将你的手机屏幕变成奇幻世界&#xff0c;带来全新视觉感受。 使用便捷性&…

docker安装kafka,并通过springboot快速集成kafka

目录 一、docker安装和配置Kafka 1.拉取 Zookeeper 的 Docker 镜像 2.运行 Zookeeper 容器 3.拉取 Kafka 的 Docker 镜像 4.运行 Kafka 容器 5.下载 Kafdrop 6.运行 Kafdrop 7.如果docker pull wurstmeister/zookeeper或docker pull wurstmeister/kafka下载很慢&#x…

前端导出word文件,并包含导出Echarts图表等

基础导出模板 const html <html><head><style>body {font-family: Times New Roman;}h1 {text-align: center;}table {border-collapse: collapse;width: 100%;color: #1118FF;font-weight: 600;}th,td {border: 1px solid black;padding: 8px;text-align: …

2024系统编程语言风云变幻:Rust持续领跑,Zig与Ada异军突起

2024年系统编程语言调查报告新鲜出炉&#xff01;这份报告对Rust、Zig、Ada、C、C等主流语言进行了全面评估&#xff0c;结果令人瞩目。Rust凭借其强大的类型系统和内存安全机制继续领跑&#xff0c;而Zig和Ada则展现出巨大的潜力&#xff0c;为系统编程领域带来了新的活力。本…

Jenkins 构建 Unity 打包 .apk 同时生成 .aab

Jenkins 构建 Unity 打包 .apk 同时生成 .aab Android App Bundle简称 AAB&#xff0c;想了解更多关于 AAB 的知识&#xff0c;请看官网 https://developer.android.google.cn/guide/app-bundle/faq?hlzh-cn APK 打包部分在复用上一篇 Jenkins 构建 Unity打包APK 一、新建一…

JAVAweb-标签选择器,盒模型,定位,浮动

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>标签</title><style type"text/css&q…

计算机视觉:主流数据集整理

第一章&#xff1a;计算机视觉中图像的基础认知 第二章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(一) 第三章&#xff1a;计算机视觉&#xff1a;卷积神经网络(CNN)基本概念(二) 第四章&#xff1a;搭建一个经典的LeNet5神经网络(附代码) 第五章&#xff1…

二级公共基础之数据结构与算法篇(五)树和二叉树

目录 前言 一、树的基本概念 1.父结点和根节点 2.子节点和叶子节点 3.度和深度 4.子树 二、二叉树及其基本性质 1. 二叉树的定义 2. 二叉树的基本性质 性质1 性质2 性质3 性质4 性质5 性质6 三、二叉树的存储结构 四、二叉树的遍历 1.遍历二叉树的概念 1. 前…

自制操作系统学习第七天

今天要做什么&#xff1f; 实现HLT&#xff0c;不让计算机处于HALT&#xff08;HLT&#xff09;.用C语言实现内存写入&#xff08;错误&#xff0c;需要分析&#xff09; 一:使用HLT&#xff0c;让计算机处于睡眠状态 写了下面这个程序&#xff0c;naskfunc.nas 函数名叫io_h…

Python Django系列—入门实例(二)

数据库配置 现在&#xff0c;打开 mysite/settings.py 。这是个包含了 Django 项目设置的 Python 模块。 默认情况下&#xff0c;​ DATABASES 配置使用 SQLite。如果你是数据库新手&#xff0c;或者只是想尝试 Django&#xff0c;这是最简单的选择。SQLite 包含在 Python 中…

DeepSeek接入Siri(已升级支持苹果手表)完整版硅基流动DeepSeek-R1部署

DeepSeek接入Siri&#xff08;已升级支持苹果手表&#xff09;完整版硅基流动DeepSeek-R1部署 **DeepSeek** 是一款专注于深度学习和人工智能的工具或平台&#xff0c;通常与人工智能、机器学习、自动化分析等领域有关。它的主要功能可能包括&#xff1a;深度学习模型搜索&…

抗辐照加固CAN FD芯片的商业航天与车规级应用解析

在工业自动化、智能汽车、航空航天及国防装备等关键领域&#xff0c;数据传输的安全性、可靠性与极端环境适应能力是技术升级的核心挑战。国科安芯推出全新一代CANFD&#xff08;Controller Area Network Flexible Data Rate&#xff09;芯片&#xff0c;以高安全、高可靠、断电…

Java数据结构第十二期:走进二叉树的奇妙世界(一)

专栏&#xff1a;数据结构(Java版) 个人主页&#xff1a;手握风云 目录 一、树型结构 1.1. 树的定义 1.2. 树的基本概念 1.3. 树的表示形式 二、二叉树 2.1. 概念 2.2. 两种特殊的二叉树 2.3. 二叉树的性质 2.4. 二叉树的存储 三、二叉树的基本操作 一、树型结构 1.…

nginx 反向代理 配置请求路由

nginx | 反向代理 | 配置请求路由 nginx简介 Nginx&#xff08;发音为“Engine-X”&#xff09;是一款高性能、开源的 Web 服务器和反向代理服务器&#xff0c;同时也支持邮件代理和负载均衡等功能。它由俄罗斯程序员伊戈尔西索夫&#xff08;Igor Sysoev&#xff09;于 2004…

ath9k(Atheros芯片)开源驱动之wifi连接

为什么会推荐这个wifi 驱动进行学习&#xff1f; ath9k&#xff08;Atheros芯片&#xff09;&#xff1a;代码结构清晰&#xff0c;适合学习实践 为什么我只在开篇写了一个wifi连接的操作&#xff1f; 先让一个开源驱动在你的硬件上跑起来&#xff0c;再逐步修改&#xff0c…

LLaMA-Factory|微调大语言模型初探索(4),64G显存微调13b模型

上篇文章记录了使用lora微调deepseek-7b&#xff0c;微调成功&#xff0c;但是微调llama3-8b显存爆炸&#xff0c;这次尝试使用qlora微调HQQ方式量化&#xff0c;微调更大参数体量的大语言模型&#xff0c;记录下来微调过程&#xff0c;仅供参考。 对过程不感兴趣的兄弟们可以直…