算法沉淀——动态规划之01背包问题(leetcode真题剖析)

news2024/12/24 8:05:39

在这里插入图片描述

算法沉淀——动态规划之01背包问题

  • 01.【模板】01背包
  • 02.分割等和子集
  • 03.目标和
  • 04.最后一块石头的重量 II

01背包问题是一类经典的动态规划问题,通常描述为:有一个固定容量的背包,以及一组物品,每件物品都有重量和价值,目标是找到在背包容量范围内,使得背包中的物品总价值最大的组合。

具体来说,问题的输入包括:

  1. 一个固定容量的背包(通常表示为一个整数W)。
  2. 一组物品,每个物品有两个属性:重量(通常表示为一个整数weight)和价值(通常表示为一个整数value)。
  3. 求解的目标是找到一种放置物品的方式,使得放入背包的物品的总重量不超过背包容量,并且总价值最大。

这个问题的特点是,对于每件物品,你只能选择将其放入背包一次(0-1,因此称为“01背包”),或者不放入背包。不能将物品切割成更小的部分放入背包,要么整个物品放入背包,要么不放入。

动态规划解法

  1. 定义状态: 通常使用二维数组dp[i][j]表示在前i个物品中,背包容量为j时的最大总价值。

  2. 状态转移方程: 考虑第i个物品,可以选择放入背包或者不放入。如果选择放入,那么总价值为dp[i-1][j-weight[i]] + value[i],即前i-1个物品的总价值加上当前物品的价值。如果选择不放入,那么总价值为dp[i-1][j],即前i-1个物品的总价值。因此,状态转移方程为:

    dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
    

    其中,dp[i-1][j]表示不放入第i个物品,dp[i-1][j-weight[i]] + value[i]表示放入第i个物品。

  3. 初始条件:i=0时,表示前0个物品,总价值为0;当j=0时,表示背包容量为0,总价值也为0。

  4. 遍历顺序: 外层循环遍历物品,内层循环遍历背包容量。

  5. 返回结果: 最终结果存储在dp[N][W]中,其中N为物品数量,W为背包容量。

例子

假设有如下物品:

Copy code解释物品1:重量=2,价值=3
物品2:重量=3,价值=4
物品3:重量=4,价值=5
物品4:重量=5,价值=6

背包容量为W=8,我们要求解在这个条件下的最大总价值。

按照上述动态规划解法,构建状态转移表如下:

luaCopy code解释  重量/价值   0   1   2   3   4   5   6   7   8
  ----------------------------------------------
  物品0        0   0   0   0   0   0   0   0   0
  物品1        0   0   3   3   3   3   3   3   3
  物品2        0   0   3   4   4   7   7   7  10
  物品3        0   0   3   4   4   7   8   8  11
  物品4        0   0   3   4   4   7   8   9  11

因此,最终结果为dp[4][8] = 11,表示在背包容量为8的情况下,最大总价值为11。这意味着最优解是选择物品2和物品4放入背包。

01.【模板】01背包

题目链接:https://www.nowcoder.com/practice/fd55637d3f24484e96dad9e992d3f62e?tpId=230&tqId=2032484&ru=/exam/oj&qru=/ta/dynamic-programming/question-ranking&sourceUrl=%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%25E7%25AF%2587%26topicId%3D196

你有一个背包,最多能容纳的体积是V。

现在有n个物品,第i个物品的体积为vi,价值为wi。

(1)求这个背包至多能装多大价值的物品?

(2)若背包恰好装满,求至多能装多大价值的物品?

输入描述

第一行两个整数n和V,表示物品个数和背包体积。

接下来n行,每行两个数vi和wi,表示第i个物品的体积和价值。

1≤n,V;vi,wi≤1000

输出描述

输出有两行,第一行输出第一问的答案,第二行输出第二问的答案,如果无解请输出0。

示例1

输入

3 5
2 10
4 5
1 4

输出

14
9

复制

说明:

装第一个和第三个物品时总价值最大,但是装第二个和第三个物品可以使得背包恰好装满且总价值最大。 

示例2

输入

3 8
12 6
11 8
6 8

输出

8
0

说明

装第三个物品时总价值最大但是不满,装满背包无解。 要求O(nV)的时间复杂度,O(V)空间复杂度

思路

第一问:

  1. 状态表示:
    • dp[i][j] 表示从前 i 个物品中挑选,总体积不超过 j 的情况下,所有的选法中能挑选出的最大价值。
  2. 状态转移方程:
    • 对于每个物品,我们有两种选择:
      • 不选第 i 个物品:此时 dp[i][j] = dp[i - 1][j]
      • 选择第 i 个物品:此时需要确保总体积不超过 j - v[i],而且该状态是合法的,即 j >= v[i]dp[i - 1][j - v[i]] 存在。状态转移方程为 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i])
  3. 初始化:
    • 多加一行,第一行初始化为 0,因为不选任何物品总体积为 0时,价值为 0
  4. 填表顺序:
    • 从上往下,每一行从左往右填表。
  5. 返回值:
    • 返回 dp[n][V],即最后一行最后一列的值。

第二问:

  1. 状态表示:
    • dp[i][j] 表示从前 i 个物品中挑选,总体积正好等于 j 的情况下,所有的选法中能挑选出的最大价值。
  2. 状态转移方程:
    • dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i])
    • 在使用 dp[i - 1][j - v[i]] 时,需要判断 j >= v[i]dp[i - 1][j - v[i]] 是否为 -1
  3. 初始化:
    • 多加一行,第一格初始化为 0,表示正好凑齐体积为 0的背包。
    • 第一行后面的格子初始化为 -1,因为没有物品,无法满足体积大于 0的情况。
  4. 填表顺序:
    • 从上往下,每一行从左往右填表。
  5. 返回值:
    • 由于最后可能凑不成体积为 V 的情况,需要特判。

代码

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N=1002;
int n,V,v[N],w[N];
int dp[N][N];

int main() {
    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++){
            dp[i][j]=dp[i-1][j];
            if(j>=v[i]) dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    cout<<dp[n][V]<<endl;

    memset(dp,0,sizeof dp);
    for(int j=1;j<=V;j++) dp[0][j]=-1;
    for(int i=1;i<=n;i++)
        for(int j=0;j<=V;j++){
            dp[i][j]=dp[i-1][j];
            if(j>=v[i]&&dp[i-1][j-v[i]]!=-1)
                dp[i][j]=max(dp[i][j],dp[i-1][j-v[i]]+w[i]);
        }
    cout<<(dp[n][V]==-1?0:dp[n][V])<<endl;
}

优化步骤:

  1. 滚动数组的应用:
    • 在01背包问题中,通过滚动数组可以删去所有的横坐标,因为状态 dp[i][j] 只依赖于上一行的状态 dp[i-1][j]dp[i-1][j-v[i]],因此只需保留一行状态。
  2. 遍历顺序修改:
    • 修改了 j 的遍历顺序,原本的遍历是从 0V,现在改为从 V0。这样做的原因是,如果从 0V 遍历,会使用当前行的 dp[i-1][j-v[i]] 的值,而我们已经在上一步的滚动数组中删除了这一行,所以需要改变遍历顺序,从 V0
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N=1002;
int n,V,v[N],w[N];
int dp[N];

int main() {
    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=V;j>=v[i];j--)
            dp[j]=max(dp[j],dp[j-v[i]]+w[i]);

    cout<<dp[V]<<endl;

    memset(dp,0,sizeof dp);
    for(int j=1;j<=V;j++) dp[j]=-1;
    for(int i=1;i<=n;i++)
        for(int j=V;j>=v[i];j--)
            if(dp[j-v[i]]!=-1)
                dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
    cout<<(dp[V]==-1?0:dp[V])<<endl;
}

02.分割等和子集

题目链接:https://leetcode.cn/problems/partition-equal-subset-sum/

给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:

输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:

输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

提示:

  • 1 <= nums.length <= 200
  • 1 <= nums[i] <= 100

思路

  1. 状态表达:
    • dp[i][j] 表示在前 i 个元素中选择,所有的选法中,能否凑成总和为 j 这个数。
  2. 状态转移方程:
    • 根据最后一个位置的元素,分两种情况讨论:
      • 不选择 nums[i]:此时是否能够凑成总和为 j 取决于前 i-1 个元素的情况,即 dp[i][j] = dp[i-1][j]
      • 选择 nums[i]:如果 nums[i] 小于等于 j,则需要看前 i-1 个元素中是否能凑成总和为 j - nums[i],即 dp[i][j] = dp[i][j] || dp[i-1][j - nums[i]]
  3. 初始化:
    • 第一行表示不选择任何元素,要凑成目标和 j,只有当目标和为 0 时才能做到,因此第一行仅需初始化第一个元素 dp[0][0] = true
  4. 填表顺序:
    • 根据状态转移方程,从上往下填写每一行,每一行的顺序是无所谓的。
  5. 返回值:
    • 根据状态表达,返回 dp[n][aim] 的值,其中 n 表示数组的大小, aim 表示要凑的目标和。
  6. 空间优化:
    • 对于 01 背包类型的问题,可以进行空间上的优化,即删除第一维,并修改第二层循环的遍历顺序。

代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size(),sum=0;
        for(int x:nums) sum+=x;
        if(sum%2) return false;

        int aim=sum/2;
        vector<vector<bool>> dp(n+1,vector<bool>(aim+1));
        for(int i=0;i<=n;i++) dp[i][0]=true;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=aim;j++){
                dp[i][j]=dp[i-1][j];
                if(j>=nums[i-1]) dp[i][j]=dp[i][j]||dp[i-1][j-nums[i-1]];
            }
        return dp[n][aim];
    }
};

空间优化

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size(),sum=0;
        for(int x:nums) sum+=x;
        if(sum%2) return false;

        int aim=sum/2;
        vector<bool> dp(aim+1);
        dp[0]=true;
        for(int i=1;i<=n;i++)
            for(int j=aim;j>=nums[i-1];j--)
                dp[j]=dp[j]||dp[j-nums[i-1]];
        return dp[aim];
    }
};

03.目标和

题目链接:https://leetcode.cn/problems/target-sum/

给你一个非负整数数组 nums 和一个整数 target

向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

输入:nums = [1], target = 1
输出:1

提示:

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • -1000 <= target <= 1000

思路

  1. 状态表示:
    • dp[i][j] 表示在前 i 个数中选,总和正好等于 j,一共有多少种选法。
  2. 状态转移方程:
    • 根据最后一个位置的元素,结合题目的要求,有两种策略:
      • 不选 nums[i]:此时凑成总和 j 的总方案数,要看在前 i-1 个元素中选,凑成总和为 j 的方案数,即 dp[i][j] = dp[i-1][j]
      • 选择 nums[i]:如果 nums[i] 小于等于 j,则需要看前 i-1 个元素中是否能凑成总和为 j - nums[i],即 dp[i][j] += dp[i-1][j - nums[i]]
  3. 初始化:
    • 需要用到上一行的数据,因此初始化第一行,表示不选择任何元素凑成目标和 j。只有当目标和为 0 时才能做到,因此第一行仅需初始化第一个元素 dp[0][0] = 1
  4. 填表顺序:
    • 根据状态转移方程,从上往下填写每一行,每一行的顺序是无所谓的。
  5. 返回值:
    • 根据状态表示,返回 dp[n][aim] 的值,其中 n 表示数组的大小, aim 表示要凑的目标和。

代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0;
        for(auto x:nums) sum+=x;
        int aim=(sum+target)/2;
        if(aim<0||(sum+target)%2) return 0;
        int n=nums.size();
        vector<vector<int>> dp(n+1,vector<int>(aim+1));
        dp[0][0]=1;
        for(int i = 1; i <= n; i++) 
            for(int j = 0; j <= aim; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if(j >= nums[i - 1]) dp[i][j] += dp[i - 1][j - nums[i - 1]];
            }
        return dp[n][aim];
    }
};

04.最后一块石头的重量 II

题目链接:https://leetcode.cn/problems/last-stone-weight-ii/

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 100

思路

  1. 状态表示:
    • dp[i][j] 表示在前 i 个元素中选择,总和不超过 j 的情况下,这些元素的最大和。
  2. 状态转移方程:
    • 根据最后一个位置的元素,结合题目的要求,有两种策略:
      • 不选 stones[i]:此时是否能够凑成总和为 j,要看在前 i-1 个元素中选,能否凑成总和为 j。根据状态表示,此时 dp[i][j] = dp[i-1][j]
      • 选择 stones[i]:这种情况下是有前提条件的,此时的 stones[i] 应该是小于等于 j。因为如果这个元素都比要凑成的总和大,选择它就没有意义。那么是否能够凑成总和为 j,要看在前 i-1 个元素中选,能否凑成总和为 j - stones[i]。根据状态表示,此时 dp[i][j] = dp[i-1][j-stones[i]] + stones[i]
  3. 初始化:
    • 由于需要用到上一行的数据,可以先将第一行初始化。
    • 第一行表示「没有石头」,因此想凑成目标和 j 的最大和都是 0
  4. 填表顺序:
    • 根据状态转移方程,从上往下填写每一行,每一行的顺序是无所谓的。
  5. 返回值:
    • 根据状态表示,找到最接近 sum / 2 的最大和 dp[n][sum / 2]
    • 返回 sum - 2 * dp[n][sum / 2],因为我们要的是两堆石头的差。

代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=0;
        for(int x:stones) sum+=x;
        int n=stones.size(),m=sum/2;
        vector<vector<int>> dp(n+1,vector<int>(m+1));
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++){
                dp[i][j]=dp[i-1][j];
                if(j>=stones[i-1]) 
                    dp[i][j]=max(dp[i][j],dp[i-1][j-stones[i-1]]+stones[i-1]);
            }
        return sum-2*dp[n][m];
    }
};

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

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

相关文章

【蓝桥杯】分巧克力

一.题目描述 二.输入描述 三.输出描述 四.问题分析 //分巧克力 #include <iostream> #include <algorithm>using namespace std;const int N1e510; int n,k,h[N],w[N];bool judge(int mid){int cnt0;for(int i0;i<n;i){cnt(h[i]/mid)*(w[i]/mid);if(cnt>k)r…

【短时交通流量预测】基于Elman神经网络

课题名称&#xff1a;基于Elman神经网络的短时交通流量预测 版本时间&#xff1a;2023-04-27 代码获取方式&#xff1a;QQ&#xff1a;491052175 或者 私聊博主获取 模型简介&#xff1a; 城市交通路网中交通路段上某时刻的交通流量与本路段前几个时段的交通流量有关&#…

数仓项目6.0(一)

尚硅谷大数据项目【电商数仓6.0】企业数据仓库项目_bilibili 数据流转过程 用户➡️业务服务器➡️数据库存储➡️数仓统计分析➡️数据可视化 数据仓库处理流程&#xff1a;数据源➡️加工数据➡️统计筛选数据➡️分析数据 数据库不是为了数据仓库服务的&#xff0c;需要…

【牛客】VL63 并串转换

题目 描述 题目描述&#xff1a; 设计一个模块进行并串转换&#xff0c;要求每四位d输为转到一位dout输出&#xff0c;输出valid_in表示此时的输入有效 信号示意图&#xff1a; clk为时钟 rst为低电平复位 valid_in 表示输入有效 d 信号输入 dout 信号输出 波形示意图&…

TypeError: the JSON object must be str, bytes or bytearray, not dict

参考文章&#xff1a;https://blog.csdn.net/yuan2019035055/article/details/124934362 Python基础系列&#xff08;一&#xff09;搞懂json数据解析与字典之间的关系 代码&#xff1a; 报错信息: TypeError: the JSON object must be str, bytes or bytearray, not dict …

光伏并网逆变器低电压穿越控制Simulink模型!

适用平台&#xff1a;MatlabSimulink 简介 当电网突发跌落故障时&#xff0c;电网电压降低会使并网电流增大、母线电压升高。当跌落程度较轻时&#xff0c;并网电流仍在逆变器安全运行的范围内&#xff1b;但跌落程度较深时&#xff0c;会引发逆变器过流和母线过压等问题&…

CommandLineRunner的使用

背景 在项目启动时需要做一些数据预加载或者某些操作&#xff0c;需要怎么办呢&#xff0c;方法其实有好几种&#xff0c;这里主要讲一下SpringBoot提供的CommandLineRunner接口的使用。一、案例说明以及实现 1.实现CommandLineRunner接口 定义一个类实现CommandLineRunner接…

在 Linux 上用 zram 替代传统交换空间 | Linux 中国

我在我的电脑上花了很多时间&#xff08;我是说工作&#xff09;&#xff0c;我发现了很多有趣的东西。其中最近引起我注意的是 zram0 设备。我是在几个月前写一篇文章时第一次注意到它&#xff0c;它显示在 lsblk 命令的输出中&#xff1a; # lsblk NAME MAJ:MIN RM…

Mybatis-Plus介绍

目录 一、Mybatis-Plus简介 1.1、介绍 1.2、特性 1.3、架构 1.4、Mybatis-Plus与Mybatis的区别 二、快速入门 2.1、首先创建数据库mybatis-plus 2.2、创建user表 2.3、插入数据 2.4、创建Spring-Boot项目 2.5、添加依赖 2.6、连接数据库 一、Mybatis-Plus简介 1.1、…

内网穿透的应用-如何修改Nginx服务location代理转发规则结合cpolar实现无公网ip环境访问内网站点

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

北京大学发布,将试错引入大模型代理学习!

引言&#xff1a;探索语言智能的新边界 在人工智能的发展历程中&#xff0c;语言智能始终是一个核心的研究领域。随着大语言模型&#xff08;LLM&#xff09;的兴起&#xff0c;我们对语言智能的理解和应用已经迈入了一个新的阶段。这些模型不仅能够理解和生成自然语言&#x…

什么是杠杆?WeTrade众汇这样举例,大家都明白

杠杆是投资交易者一定要知道的一个金融术语。那么什么是杠杆呢?下面WeTrade众汇就用苹果进行举例&#xff0c;大家就都会明白&#xff0c;原来如此简单。 发挥我们投资者的想象&#xff0c;我们现在要进行一场苹果的买卖&#xff0c;能够赚钱的本质就是高买低卖&#xff0c;所…

快速搭建Vue前端框架

快速搭建Vue前端框架 安装Vue Vue官方安装过程:https://cli.vuejs.org/zh/guide/installation.html 二.创建Vue工程 2.2 安装淘宝镜像 安装淘宝镜像&#xff08;会让你安装Vue的速度加快&#xff09;&#xff1a; npm config set registry https://registry.npm.taobao.or…

Java进阶(锁)——锁的升级,synchronized与lock锁区别

目录 引出Java中锁升级synchronized与lock锁区别 缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java进阶&#xff08;锁&#xff09;——锁的升级&#xff0c;synchronized与lock锁区别 Java中锁升级 看一段代码&#xff1a; public class…

IDE插件-通义灵码-用AI写代码

安装tongyi插件 右边就出现这个插件了&#xff0c;用支付宝账号登录成功后就出现一个AI机器人了 选中代码右键就能看到他的常规功能 提问模式 智能代码联想&#xff0c;根据你上一句代码&#xff0c;点回车&#xff0c;等一会&#xff0c;自动给你下语句想要的代码&#xff0c;…

【毕业论文小记】从Peer下载到近断层脉冲地震动生成——基于一个完全免费的地震波生成Python程序

如果因为TA和游戏相关关注我的朋友们&#xff0c;看到这篇可以不用继续往下看了啊啊啊啊&#xff01;不是跑路了不是跑路了&#xff01;毕业论文需要&#xff08;本专业土木人的心酸&#xff09;&#xff01;&#xff01; 在写毕业论文的时候&#xff0c;进行近断层脉冲地震动…

cleanmymacX破解版2024最新版下载

摘要 对于很多Mac用户来说&#xff0c;知您网分享的CleanMyMac X Mac破解版是大家首选的优秀Mac清理软件。由于它强大的功能&#xff0c;让大量新老用户所折服。作为老牌开发商制作的优秀应用&#xff0c;CleanMyMac X破解版几乎满足用户所有的清理需求。这款清理软件不仅包含…

OpenShift AI - 部署并使用 LLM 模型

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.15 RHODS 2.7.0 的环境中验证 文章目录 安装 OpenShift AI 环境安装 Minio 对象存储软件配置 Single Model Serving 运行环境创建项目和 Workbench准备模型和配置 Model Server访问 LLM 模…

python-分享篇-生成仿微信公众号推广的个性二维码(支持动态)

代码 生成仿微信公众号推广的个性二维码&#xff08;支持动态&#xff09;from MyQR import myqr # 要生成动态二维码&#xff0c;只需要将piture参数和save_name参数设置gif动图即可 myqr.run(wordshttps://blog.csdn.net/stqer/article/details/135553200, # 指定二维码包含…

制药企业制药设备如何进行设备维护与设备管理

在制药企业的生产中&#xff0c;制药设备的维护与管理是保障整个生产链顺利运转的重要环节。制药行业面临着诸多挑战&#xff0c;尤其是在涉及众多化学药品的生产过程中&#xff0c;对设备的要求更为严格。为了确保企业的稳健发展&#xff0c;制药企业需采取一系列措施&#xf…