完全背包问题,原理剖析,公式推导,OJ详解

文章目录

    • 前言
    • 一、完全背包的状态设计
      • 1、状态设计
      • 2、状态转移方程
      • 3、对比0/1背包问题
      • 4、时间复杂度分析
    • 二、完全背包问题的优化
      • 1、时间复杂度优化
      • 2、空间复杂度优化
    • 三、OJ练习
      • 裸题
      • 完全背包+离散化
      • 最小值

前言

完全背包问题,相比0/1背包问题,实就每个物品可以取无限次。


一、完全背包的状态设计

有n(n≤100)种物品和一个容量为m(m≤10000)的背包。第i种物品的容量是c[i],价值是w[i]。现在需要选择一些物品放入包, 每种物品可以无限选择,组总容量不能超过背包容量,求能够达到的物品的最大总价值。

以上就是完全背包问题的完整描述,和0/1背包的区别就是每种物品可以无限选取;

1、状态设计

状态(i , j)表示前 i 种物品恰好放入容量为 j 的背包(0 ≤ i < n, 0 ≤ j ≤ m);令 dp[i][j]示状态(i, j)下该背包得到的最大价值,即前 i 种物品(每种物品可以选择无限件)恰好放入容量为j的背包所得到的最大总价值;

2、状态转移方程

d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j − c [ i ] ∗ k ] + w [ i ] ∗ k ) ( 0 ≤ k ≤ j / c [ i ] ) dp[i][j] = max(dp[i- 1][j -c[i]*k] + w[i] *k) (0≤k≤j/c[i]) dp[i][j]=max(dp[i1][jc[i]k]+w[i]k)(0kj/c[i])

因为每种物品有无限种可放置,将它归类为以 下两种情况:

  • 不放:如果“第 i 种物品不放入容量为 j 的背包",那么问题转化成求**" 前i - 1种物品放入容量为 j 的背包"的问题,所以最大价值就等于"前i - 1种物品放入容量为 j 的背包"的最大价值**,对应状态转移方程中k = 0的情况,即dp[i-1][j]
  • 放k个:如果第 i 种物品放入容为j的背包",那么问题转化成求"前 i - 1 种物品放入容量为j - c[i] * k的背包"的问题;那么此时最大价值就等于"前i - 1种物品放入容量为j-c[i] * k的背包"的最大价值加上放入 k 个第 i 种物品的价值,即dp[i-1][j - c[i] * k] + w[i] * k
    枚举所有满足条件的 k 就是我们所求的 “前i种物品恰好放入容为j的背包” 的最大价值了。
    注意:由于每件物品都可以无限选择,所以这里描述的时候都是用的"种"作为单位,即代表不同种类的物品。

3、对比0/1背包问题

完全背包问题是0/1包问题的扩展,区别就是它可以选择取0件、取1件、取2 件、取…k件,而0/1包问题只能取0件、取1件。如果从状态转移方程出发,我们可以把两个问题进行归纳统一,得到一个统一的状态转移方程如下:
d p [ i ] [ j ] = m a x ( d p l i − 1 ] [ j − c [ i ] ∗ k ] + w [ i ] ∗ k ) dp[i][j]= max(dpli- 1][j-c[i]*k]+ w[i]*k) dp[i][j]=max(dpli1][jc[i]k]+w[i]k)

  • 对于0/1包问题,k的取值为0, 1
  • 对于完全背包问题,k的取值为0, 1, 2, 3, ……, j / c[i];

4、时间复杂度分析

对于n种物品放入一个容量为m的背包,状态数为O(nm),每次状态转移的消耗为O(k),所以整个状态转移的过程时间复杂渡是大于O(nm)的,那么实际是多少呢?
考虑最坏情况下,即c[i]= 1时,那么要计算的dp[][j]的转移数为 nm ,**总的状态转移次数就是nm2**,所以整个算法的时间复杂度是O(nm2)的,也就是说状态转移均摊时间复杂度是O(m)的。
接下来一节会对完全背包问题的时间和空间复杂度进行优化。

二、完全背包问题的优化

1、时间复杂度优化

我们把状态转移方程进行展开后得到如下的k+1个式子:
d p [ i ] [ j ] = m a x { d p [ i − 1 ] [ j ] ( 1 ) d p [ i − 1 ] [ j − c [ i ] ] + w [ i ] ( 2 ) d p [ i − 1 ] [ j − c [ i ] ∗ 2 ] + w [ i ] ∗ 2 ( 3 ) . . . d p [ i − 1 ] [ j − c [ i ] ∗ k ] + w [ i ] ∗ k ( k + 1 ) dp[i][j]\,=max\left\{\begin{align} &dp[i-1][j]&(1)\\ &dp[i-1][j-c[i]]+w[i]&(2)\\ &dp[i-1][j-c[i]*2]+w[i]*2&(3)\\ &...\\ &dp[i-1][j-c[i]*k]+w[i]*k&(k+1)\\ \end{align}\right. dp[i][j]=max dp[i1][j]dp[i1][jc[i]]+w[i]dp[i1][jc[i]2]+w[i]2...dp[i1][jc[i]k]+w[i]k(1)(2)(3)(k+1)
利用待定系数法,用j - c[i]代替上式的 j 得到如下式子:
d p [ i ] [ j − c [ i ] ] = m a x { d p [ i − 1 ] [ j − c [ i ] ] ( 1 ) d p [ i − 1 ] [ j − c [ i ] ∗ 2 ] + w [ i ] ( 2 ) d p [ i − 1 ] [ j − c [ i ] ∗ 3 ] + w [ i ] ∗ 2 ( 3 ) . . . d p [ i − 1 ] [ j − c [ i ] ∗ k ] + w [ i ] ∗ ( k − 1 ) ( k ) dp[i][j-c[i]]\,=max\left\{\begin{align} &dp[i-1][j-c[i]]&(1)\\ &dp[i-1][j-c[i]*2]+w[i]&(2)\\ &dp[i-1][j-c[i]*3]+w[i]*2&(3)\\ &...\\ &dp[i-1][j-c[i]*k]+w[i]*(k-1)&(k)\\ \end{align}\right. dp[i][jc[i]]=max dp[i1][jc[i]]dp[i1][jc[i]2]+w[i]dp[i1][jc[i]3]+w[i]2...dp[i1][jc[i]k]+w[i](k1)(1)(2)(3)(k)
等式两边都加上w[]得到:
d p [ i ] [ j − c [ i ] ] + w [ i ] = m a x { d p [ i − 1 ] [ j − c [ i ] ] + w [ i ] ( 1 ) d p [ i − 1 ] [ j − c [ i ] ∗ 2 ] + w [ i ] ∗ 2 ( 2 ) d p [ i − 1 ] [ j − c [ i ] ∗ 3 ] + w [ i ] ∗ 3 ( 3 ) . . . d p [ i − 1 ] [ j − c [ i ] ∗ k ] + w [ i ] ∗ k ( k ) dp[i][j-c[i]]+w[i] \, = max \left\{\begin{align} &dp[i-1][j-c[i]]+w[i]&(1)\\ &dp[i-1][j-c[i]*2]+w[i]*2&(2)\\ &dp[i-1][j-c[i]*3]+w[i]*3&(3)\\ &...\\ &dp[i-1][j-c[i]*k]+w[i]*k&(k)\\ \end{align}\right. dp[i][jc[i]]+w[i]=max dp[i1][jc[i]]+w[i]dp[i1][jc[i]2]+w[i]2dp[i1][jc[i]3]+w[i]3...dp[i1][jc[i]k]+w[i]k(1)(2)(3)(k)
于是我们发现,这里的(1…k)式等价于最开始的状态转移方程中的(2) … (k+1)式,所以原状态转移方程可以简化为:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − c [ i ] ] + w [ i ] ) dp[i][j]\,=max(dp[i-1][j],dp[i][j-c[i]]+w[i]) dp[i][j]=max(dp[i1][j],dp[i][jc[i]]+w[i])

这样就把原本均摊时间复杂度为O(m)的状态转移优化到了O(1)。
那么我们来理解一下这个状态转移方程的含义:
对于第i种物品,其实只有两种选择: 一个都不放、至少放一个。

  • 一个都不放就是"前i - 1种物品放满一个容量为 j 的背包"的情况,即dp[i-1][i]

  • 至少放一个的话,我们在”前i种物品装满j - c[i]容量的背包“的情况中再塞一个第i种物品,就能保证至少放一个第i种物品了,此时的价值为dp[i][j - c[i]] + w[i],最大价值即二者取大。

如果只是为了掌握完全背包的模板,那么这个公式可以开头就讲,但是许多动态规划问题都不是裸题,需要我们从问题本身入手一步步的分析,优化从而解决问题,所以了解原理及公式推导是很有必要的。

2、空间复杂度优化

空间复杂度优化不用说就能猜到一定又是滚动数组优化,因为状态转移方程提示的已经很明显了,我们仍然从状态图上来观察,是否可以进行滚动数组优化。

根据优化后的状态转移方程,我们发现状态转移的过程如图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

蓝色的格子代表的是已经计算出来的状态,红色的格子代表的是当前正在计算的状态,即dp[i][j],它的值来自dp[i-1][j]和dp[i][j-c[i]],这两个值对应的格子一定是蓝色的,绿色色的格子代表尚未进行计算的状态;

为了将问题描述的更加清晰,我们把(i, j)看成是二维笛卡尔坐标系上的点,i在x轴上, j在y轴上,如图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

任何一个状态在计算出来以后,只会给x坐标比它大或者y坐标比它大的使用,所以我们只需要保留一行状态,按照x递增进行顺序计算,就可以做到把二维的问题转换成一维,将状态转移方程变成如下表示:
d p [ j ] = m a x ( d p [ j ] , d p [ j − c [ i ] ] + w [ i ] ) dp[j]\,=max(dp[j],dp[j-c[i]]+w[i]) dp[j]=max(dp[j],dp[jc[i]]+w[i])

三、OJ练习

裸题

P1616 疯狂的采药 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

友情提示:不开long long见祖宗

很直白的裸题,t就是背包容量,给了m种物品,求最大价值。

直接跑板子,最后一个点不开long long会错

AC代码如下:

#include <iostream>
#include <cstring>
using namespace std;
#define N 10010
#define M 10000010
#define int long long
int t, m;
int c[N], w[N], dp[M]{0};signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);cin >> t >> m;for (int i = 0; i < m; i++)cin >> c[i] >> w[i];for (int i = 0; i < m; i++)for (int j = c[i]; j <= t; j++)dp[j] = max(dp[j], dp[j - c[i]] + w[i]);cout << dp[t];return 0;
}

[P2722 USACO3.1] 总分 Score Inflation - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

同样裸题,直接跑板子即可。

AC代码如下:

#include <iostream>
#include <cstring>
using namespace std;
#define N 10010
#define M 10010
#define int long long
int t, m;
int c[N], w[N], dp[M]{0};signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);cin >> t >> m;for (int i = 0; i < m; i++)cin >> w[i] >> c[i];for (int i = 0; i < m; i++)for (int j = c[i]; j <= t; j++)dp[j] = max(dp[j], dp[j - c[i]] + w[i]);cout << dp[t];return 0;
}

完全背包+离散化

P1853 投资的最大效益 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

根据题目呢,如果我们不考虑n年后的情况,只考虑第一年能够得到的最大资产,那么很简单,就是一个完全背包问题的裸题,我们把债券当作物品,初始总资产当作容量,求出最大收益,然后最大收益加上初始总资产就是第一年能够得到的最大资产

那么n年的情况呢?

我们发现他这个债券机制太科幻了,我们每年得到收益后还能原价卖出债券,下一年再拿新的资产去选择新的债券购买方案,虽然不知道现实中有没有这种好事,但是对于我们做题而言反而简单了。

我们第一年结束得到了新的资产,第二年拿着第一年的资产再来一次完全背包,年底再卖掉,第三年拿着第二年的资产再来……

我们发现所谓的n年就是让你跑n次完全背包罢了

到这里可以直接写板子了,加一层循环罢了。但是!!!你的资产越来越大,也就是说你的状态数组dp的空间就有要求了,很可能再某一年你的背包直接爆炸了

我们发现题目里说了债券的价格为1000的倍数,那么我们就可以将数据离散化了

状态转移过程中把容量除以1000,物品体积也除以1000,价值不做改变,这样我们原先最大价值为dp[s],这样一来就变成了dp[s/1000]了,就有了下面的代码

AC代码如下:

#include <iostream>
#include <cstring>
using namespace std;
#define N 10010
#define M 1000010
#define int long long
int s, n, d;
int c[N], w[N], dp[M]{0};signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);// freopen("in.txt", "r", stdin);// freopen("out.txt", "w", stdout);cin >> s >> n >> d;for (int i = 0; i < d; i++){cin >> c[i] >> w[i];}for (int k = 0; k < n; k++){memset(dp, 0, sizeof(dp));for (int i = 0; i < d; i++)for (int j = c[i] / 1000; j <= s / 1000; j++)dp[j] = max(dp[j], dp[j - c[i] / 1000] + w[i]);s += dp[s / 1000];}cout << s;return 0;
}

最小值

[P2918 USACO08NOV] Buying Hay S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

不难想到这是个完全背包的问题,那么对问题进行抽象,怎样选取物品和容量呢?这里给出两种角度,两种角度有着不同的处理细节。

F1 每个公司package的重量为价值,价格为体积

这样思考其实是很简单粗暴,也是最好理解的,我们即然要求购买干草量不小于目标值的所有方案中的最小花费,那么我们设定一个总花费的上限,然后以上限为总容量,以每个公司的包裹为n种物品,其重量为价值,价格为体积

这样我们跑一遍完全背包后,只要找到满足dp[j] >= s的最小j即可(s为需要购买的干草重量)

显然dp[j]是单调递增的,钱越多买的越多嘛,所以直接二分查找答案并输出即可。

#include <iostream>
#include <cstring>
using namespace std;
#define N 110
#define M 100000
#define int long long
int s, n, ans = M;
int c[N], w[N], dp[M + 1]{0};signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);freopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);cin >> n >> s;for (int i = 0; i < n; i++)cin >> w[i] >> c[i];for (int i = 0; i < n; i++)for (int j = c[i]; j <= M; j++)dp[j] = max(dp[j], dp[j - c[i]] + w[i]);cout << (lower_bound(&dp[0], &dp[M], s) - &dp[0]);return 0;
}

F2 每个公司package的价格为价值,重量为体积

这样的话,先不考虑具体代码的实现,最终我们的答案就是dp[s]了吗?

不一定,很可能实际情况情况导致我们不能恰购买s的干草,即物品的体积导致容量s背包装不满,此时dp[s]为非法值,所以我们最终要找到一个dp[j]最小的j(j>=s)

这样的话我们由于求的是最小花费,所以这里状态转移的时候要转移最小值,所以不可避免地要对dp数组预处理为很大的非法值。

初态dp[0] = 0,背包容量的上限为s + 5000,这个根据数据范围很好得出

直接上代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 110
#define M 55010
#define int long long
int s, n, ans = M;
int c[N], w[N], dp[M]{0};signed main()
{ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);//freopen("in.txt", "r", stdin);//freopen("out.txt", "w", stdout);cin >> n >> s;for (int i = 0; i < n; i++)cin >> c[i] >> w[i];memset(dp, 0x3f, sizeof(dp));dp[0] = 0;for (int i = 0; i < n; i++)for (int j = c[i]; j <= s + 5000; j++)dp[j] = min(dp[j], dp[j - c[i]] + w[i]);cout << *min_element(&dp[s], &dp[s + 5001]);return 0;
}

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

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

相关文章

深度学习 | DRNN、BRNN、LSTM、GRU

1、深度循环神经网络 1.1、基本思想 能捕捉数据中更复杂模式并更好地处理长期依赖关系。 深度分层模型比浅层模型更有效率。 Deep RNN比传统RNN表征能力更强。 那么该如何引入深层结构呢&#xff1f; 传统的RNN在每个时间步的迭代都可以分为三个部分&#xff1a; 1.2、三种深层…

如何将语音版大模型AI接入自己的项目里(语音ChatGPT)

如何将语音版大模型AI接入自己的项目里语音ChatGPT 一、语音版大模型AI二、使用步骤1、接口2、请求参数3、请求参数示例4、接口 返回示例5、智能生成API代码 三、 如何获取appKey和uid1、申请appKey:2、获取appKey和uid 四、重要说明 一、语音版大模型AI 基于阿里通义千问、百…

IT安全:实时网络安全监控

了解庞大而复杂的网络环境并非易事&#xff0c;它需要持续观察、深入分析&#xff0c;并对任何违规行为做出快速反应。这就是为什么实时网络安全监控工具是任何组织 IT 安全战略的一个重要方面。 网络攻击和合规性法规是 IT 安全的两个主要驱动因素。同时&#xff0c;数据泄露…

关于“Python”的核心知识点整理大全45

目录 15.4.6 绘制直方图 die_visual.py 注意 15.4.7 同时掷两个骰子 dice_visual.py 15.4.8 同时掷两个面数不同的骰子 different_dice.py 15.5 小结 第 16 章 16.1 CSV 文件格式 16.1.1 分析 CSV 文件头 highs_lows.py 注意 16.1.2 打印文件头及其位置 highs_l…

Redis 核心知识总结

Redis 核心知识总结 认识 Redis 什么是 Redis&#xff1f; Redis 是一个由 C 语言开发并且基于内存的键值型数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。 有以下几个特…

案例189:基于微信小程序的高校教务管理系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder …

Python办公自动化Day3-python-docx

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;新建打开保存新建与保存打开 操作段落添加段落/查询段落数删除段落内容但未删除段落插入段落插入分页符 段落样式对齐方式自带样式缩进与间距 Run介绍/读取Run添加/修改/删除RunRun样式 小案例 文章声明⭐⭐⭐ 该文章为…

通过Python将PDF转为文本,快速提取PDF中的文字

快速高效地从PDF文档中提取信息对于专业人士来说非常重要。处理大量PDF文件时&#xff0c;将PDF转换为可编辑的文本格式可以节省时间和精力。而强大的Python语言正是在这些方面发挥其作用。利用Python中丰富的API&#xff0c;我们可以轻松在Python程序中将PDF转换为文本&#x…

《深入理解C++11:C++11新特性解析与应用》笔记四

第四章 新手易学&#xff0c;老兵易用 4.1 右尖括号>的改进 在 C98 中&#xff0c;有一条需要程序员规避的规则:如果在实例化模板的时候出现了连续的两个右尖括号 >&#xff0c;那么它们之间需要一个空格来进行分隔&#xff0c;以避免发生编译时的错误。C98 会将>&g…

MidJourney笔记(9)-daily_theme-docs-describe

/daily_theme 切换 #daily-theme 频道更新的通知。 但我发现在对话框那里,是没有这个命令的: 但官网是有介绍,不知道是不是版本问题还是这个命令已经无效。 但后来,我发现这个命令是要在Midjourney服务对话框那里才有,在我们后面添加的Mid

Linux的账号及权限管理

一.管理用户账号 1.1 用户账户的分类 1.1.1 用户账号的分类 超级用户&#xff1a;&#xff08;拥有至高无上的权利&#xff09; root用户是Linux操作系统中默认的超级用户账号&#xff0c;对本主机拥有最高的权限&#xff0c;系统中超级用户是唯一的。普通用户&#xff1a; …

2023年12月电子学会Scratch图形化编程一级真题及答案

2023年12月电子学会Scratch图形化编程一级真题及答案 一、单选题(共25题&#xff0c;共50分) 1. 观察下列每个圆形中的四个数&#xff0c;找出规律&#xff0c;在括号里填上适当的数&#xff1f;&#xff08; &#xff09; A. 9 B. 17 C. 21 D. 5 试题编号&#xff1a;202306…

信息网络协议基础-IPv6协议

文章目录 概述为什么引入IP服务模型IPv4的可扩展性问题解决方法***CIDR(Classless Inter-Domain Routing, 无类别域间寻路)前缀汇聚***前缀最长匹配***NAT(网络地址转换)存在的问题解决方案路由表配置***局限性IPv6协议头标IPv6地址表示前缀类型单播地址链路局部地址(Link-Loca…

第十二章:实验案例:使用rsync构建镜像网站

实验环境 某公司在深圳、北京两地各放置了---台网站服务器&#xff0c;分别应对南北大区内不断增长的客户访问需求&#xff0e;两台服务器的网站文档必须保持--致&#xff0c;如图12.3所示&#xff0c;同步链路已通过VPN专用线路实现。 需求描述 1&#xff0c;服务器A(北京〉…

k8s快速搭建

VMware16Pro虚拟机安装教程VMware16.1.2安装及各版本密钥CentOS7.4的安装包:提取码&#xff1a;lp6qVMware搭建Centos7虚拟机教程 搭建完一个镜像 关机 拍摄一个快照,克隆两个作为子节点 0. 环境准备 在开始之前&#xff0c;部署Kubernetes集群机器需要满足以下几个条件&#…

使用Python Flask搭建一个简单的Web站点并发布到公网上访问

文章目录 前言1. 安装部署Flask并制作SayHello问答界面2. 安装Cpolar内网穿透3. 配置Flask的问答界面公网访问地址4. 公网远程访问Flask的问答界面 前言 Flask是一个Python编写的Web微框架&#xff0c;让我们可以使用Python语言快速实现一个网站或Web服务&#xff0c;本期教程…

【linux】touch的基本使用

碎碎念 刚接触linux时候的几个最基础的命令之一&#xff0c;用来创建文件。如果使用touch --help的时候会发现作者对于touch的简介&#xff1a;Update the access and modification times of each FILE to the current time.用于修改文件的访问和时间戳 带我的leader属于那种…

Mediapipe绘制实时3d铰接骨架图——Mediapipe实时姿态估计

一、前言 大约两年前&#xff0c;基于自己的理解我曾写了几篇关于Mediapipe的文章&#xff0c;似乎帮助到了一些人。这两年&#xff0c;忙于比赛、实习、毕业、工作和考研。上篇文章已经是一年多前发的了。这段时间收到很多私信和评论&#xff0c;请原谅无法一一回复了。我将尝…

vue3老项目如何引入vite

vue3老项目如何引入vite 安装 npm install vite vitejs/plugin-vue --save-dev Vite官方中文文档修改package.json文件 在 npm scripts 中使用 vite 执行文件 "scripts": {"serve": "vite","build": "vite build","pr…

关于Redis面试题

前言 之前为了准备面试&#xff0c;收集整理了一些面试题。 本篇文章更新时间2023年12月27日。 最新的内容可以看我的原文&#xff1a;https://www.yuque.com/wfzx/ninzck/cbf0cxkrr6s1kniv Redis 是什么 全名&#xff1a;远程字典服务。这是一个开源的在内存中的数据结构存…