【代码随想录】【动态规划】背包问题 - 完全背包

完全背包

模板:完全背包问题

问题描述

完全背包问题与01背包问题唯一的区别在于:

  • 在01背包中:每个物品只有一个,要么放入背包,要么不放入背包
  • 在完全背包中:每个物品有无限多个,可以不放入背包,也可以多次放入背包

解法一:二维dp

(加粗部分是和01背包中有区别的部分)

dp[i][j]:将下标小于等于 i 的物品放入容量为 j 的背包中所能取得的最大价值

在计算dp[i][j]时:对于下标为 i 的物品进行讨论:

  • 如果放不下物品 i(当前背包容量小于物品 i 的体积,j < v[i]):dp[i][j] = dp[i-1][j] ,即将下标小于等于 i-1 的物品放入容量为 j 的背包中所能取得的最大价值
  • 如果能放下物品 i(j >= v[i]):
    • 有两种选择:
      • 不放入物品 i :dp[i][j] = dp[i-1][j]
      • 放入物品 i :dp[i][j] = w[i] + dp[i][j-v[i]] 在本次放入物品i之前背包中可以已经有物品i了
    • 从二者中选择能取得的最大价值更大的一个:dp[i][j] = max{dp[i-1][j], w[i] + dp[i][j-v[i]]}

递推公式
d p [ i ] [ j ] = { j < v [ i ] : d p [ i − 1 ] [ j ] j ≥ v [ i ] : max ⁡ ( d p [ i − 1 ] [ j ] , w [ i ] + d p [ i ] [ j − v [ i ] ] ) dp[i][j] = \left\{\begin{matrix} j < v[i]: & dp[i-1][j]\\ j \ge v[i]: & \max (dp[i-1][j], w[i] + dp[i][j-v[i]]) \end{matrix}\right. dp[i][j]={j<v[i]:jv[i]:dp[i1][j]max(dp[i1][j],w[i]+dp[i][jv[i]])

计算顺序
在计算dp[i][j]时可能会用到dp[i-1][j]位置和dp[i][0…j-1]位置的值,在计算dp[i][j]之前要保证这些位置的值已经计算过了。

在这里插入图片描述

下面几种计算顺序都可以实现这一目标:

  • 外循环遍历物品,内循环遍历背包容量 / 一行一行计算
    • 每一行从左向右计算
  • 外循环遍历背包容量,内循环遍历物品 / 一列一列计算
    • 每一列从上向下计算

在计算每一行时必须按照从左向右的顺序计算,因为计算dp[i][j]时会用到本行该位置之前的数据(dp[i][0…j-1])。

但是在计算每一列时必须按照从上向下的顺序计算,因为计算dp[i][j]时会用到本行该位置之前的数据(dp[i-1][j])。

初始化
不同的计算顺序需要初始化的内容有所不同:

  • 外循环遍历物品,内循环遍历背包容量 / 一行一行计算:既需要初始化第一行,又需要初始化第一列
  • 外循环遍历背包容量,内循环遍历物品 / 一列一列计算:既需要初始化第一行,又需要初始化第一列

具体来说:

  • 对第一行的初始化:i=0, j=0->V
    • dp[0][j]:在容量为j的背包中放物品0
      • j < v[0]:放不下,dp[0][j] = 0
      • j >= v[0]:能放下,dp[0][j] = w[0]
  • 对第一列的初始化:i=0->n-1, j=0
    • dp[i][0]:在容量为0的背包中放物品,dp[i][0] = 0
public int packageComplete(){
    // dp[i][j]: 将下标小于等于 i 的物品放入容量为 j 的背包中所能取得的最大价值
    int[][] dp = new int[n][V+1];

    // 初始化第一行: dp[0][0...j]
    for(int j = 0; j <= V; j++){
        // dp[0][j]:在容量为j的背包中放物品0
        if(j < v[0]){
            dp[0][j] = 0;    // 放不下物品0
        }else{
            // 能放下物品0
            dp[0][j] = dp[0][j-v[0]] + w[0]; 
        }
    }
    // 初始化第一列:dp[0...n][0]
    for(int i = 0; i < n; i++){
        // 在容量为0的背包里放物品,最大价值必然为0
        dp[i][0] = 0;
    }

    // 递推计算: 外循环遍历物品,内循环遍历背包容量
    for(int i = 1; i < n; i++){
        for(int j = 0; j <= V; j++){
            if(j < v[i]){
                // 放不下物品i
                dp[i][j] = dp[i-1][j];
            }else{
                // 能放下物品i
                dp[i][j] = Math.max(
                    dp[i-1][j],             // 不放入物品i
                    w[i] + dp[i][j-v[i]]    // 放入物品i(此时背包中可以已有物品i)
                );
            }
        }
    }

    // 返回结果
    return dp[n-1][V];
}

解法二:一维dp / 滚动数组

观察二维dp的递推公式发现:在计算dp[i][j]时会用到前一行的第j个数据,和当前行的前j-1个数据
d p [ i ] [ j ] = { j < v [ i ] : d p [ i − 1 ] [ j ] j ≥ v [ i ] : max ⁡ ( d p [ i − 1 ] [ j ] , w [ i ] + d p [ i ] [ j − v [ i ] ] ) dp[i][j] = \left\{\begin{matrix} j < v[i]: & dp[i-1][j]\\ j \ge v[i]: & \max (dp[i-1][j], w[i] + dp[i][j-v[i]]) \end{matrix}\right. dp[i][j]={j<v[i]:jv[i]:dp[i1][j]max(dp[i1][j],w[i]+dp[i][jv[i]])

如果我们按照 “一行一行计算,每一行从左往右计算” 的顺序进行二维dp的计算:

...
在计算dp[i][j-1]时会用到dp[i-1][j-1]和dp[i][0], dp[i][1]...dp[i][j-2]
在计算dp[i][j]  时会用到dp[i-1][j]  和dp[i][0], dp[i][1]...dp[i][j-2], dp[i][j-1]
在计算dp[i][j+1]时会用到dp[i-1][j+1]和dp[i][0], dp[i][1]...dp[i][j-2], dp[i][j-1], dp[i][j]
...

观察发现,在计算完dp[i][j]之后,就再也用不到dp[i-1][j]

综上所述,我们可以只用一行(即一个长度为V+1的数组)作为dp数组,滚动存储每一行的计算结果,不再需要的数据被新计算的结果覆盖使用。

  • 一开始先将该数组的内容初始化为原二维dp的第一行的内容
  • 在计算原二维dp的第 i 行( 2 ≤ i < n 2 \leq i < n 2i<n)时:
    • 计算开始前:滚动数组中正好是原二维dp第 i-1 行的内容
    • 从左向右计算for(int j=0; j<=V; j++)
    • 递推公式修改为: d p [ j ] = { j < v [ i ] : d p [ j ] j ≥ v [ i ] : max ⁡ ( d p [ j ] , w [ i ] + d p [ j − v [ i ] ] ) dp[j] = \left\{\begin{matrix} j < v[i]: & dp[j]\\ j \ge v[i]: & \max (dp[j], w[i] + dp[j-v[i]]) \end{matrix}\right. dp[j]={j<v[i]:jv[i]:dp[j]max(dp[j],w[i]+dp[jv[i]])
    • 由递推公式计算出的 dp[j] 实际上就是原二维dp中的 dp[i][j],而此时滚动数组中下标为 j 的位置存放的是 dp[i-1][j],这个数值在之后的计算中都用不到了,因此将其覆盖用于存储 dp[i][j]。
public int package01(){
    // dp[j]: 将物品放入容量为 j 的背包中所能取得的最大价值
    int[] dp = new int[V+1];

    // 初始化第一行: 在容量为j的背包中放物品0
    for(int j = 0; j <= V; j++){
        if(j < v[0]){
            dp[j] = 0;    // 放不下物品0
        }else{
            // 能放下物品0
            dp[j] = dp[j-v[0]] + w[0]; 
        }
    }

    // 递推计算: 外循环遍历物品,内循环遍历背包容量
    for(int i = 1; i < n; i++){
        // 原二维dp中的第一列数据,每行遇到时再初始化
        dp[0] = 0;
        // 计算这一行的剩余数据
        for(int j = 1; j <= V; j++){
            // if(j < v[i]){
            //     // 放不下物品i
            //     dp[j] = d[j]; // 什么也没做
            // }else{
            //     // 能放下物品i
            //     dp[j] = Math.max(
            //         dp[j],               // 不放入物品i
            //         w[i] + dp[j-v[i]]    // 放入物品i(此时背包中可以已有物品i)
            //     );
            // }
            if(j >= v[i]){
                dp[j] = Math.max(dp[j], w[i] + dp[j-v[i]]);
            }
        }
    }

    // 返回结果
    return dp[V];
}

518. 零钱兑换 Ⅱ

需要注意当总金额j = 0时,认为只有一种组合方式:什么也不选。

代码实现:二维dp

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;

        // dp[i][j]: 从下标小于i的硬币面额中选择,恰好凑够总金额j的组合数
        int dp[][] = new int[n][amount+1];

        // 初始化第一行:只用面额为coins[0]的硬币来凑
        dp[0][0] = 1; // 总金额为0时只有一种组合方式:什么也不选
        for(int j = 1; j <= amount; j++) {
            if(j < coins[0]){
                // 总金额j小于coins[0] => 不能凑成总金额j
                dp[0][j] = 0;
            }else{
                // 总金额j大于等于coins[0] => 如果减去当前面额后的总金额j-coins[0]不能正好凑齐,那么总金额j也无法正好凑齐;否则,先凑够j-coins[0]后再加上当前硬币面额即可,总组合数等于dp[0][j-coins[0]]
                dp[0][j] = (dp[0][j-coins[0]] == 0 ? 0 : dp[0][j-coins[0]]);
            }
        }

        // 初始化第一列:凑成总金额0 => 只有一种组合方式:什么也不选
        // dp[0][0]已经在前面初始化过了
        for(int i = 1; i < n; i++){
            dp[i][0] = 1;
        }

        // 递推计算
        for(int i = 1; i < n; i++){
            for(int j = 1; j <= amount; j++){
                if(j < coins[i]){
                    // 总金额j小于coins[i] => 不能选择coins[i]
                    dp[i][j] = dp[i-1][j];
                }else{
                    // 总金额j大于等于coins[i] => 可以选择coins[i],也可以不选择coins[i],两种加起来为总组合数
                    dp[i][j] = dp[i-1][j] 
                             + (dp[i][j-coins[i]] == 0 ? 0 : dp[i][j-coins[i]]);
                }
            }
        }

        // 返回结果
        return dp[n-1][amount];
    }
}

代码实现:一维dp

class Solution {
    public int change(int amount, int[] coins) {
        int n = coins.length;

        // dp[j]: 恰好凑够总金额j的组合数
        int dp[] = new int[amount+1];

        // 初始化第一行:只用面额为coins[0]的硬币来凑
        dp[0] = 1; // 总金额为0时只有一种组合方式:什么也不选
        for(int j = 1; j <= amount; j++) {
            if(j < coins[0]){
                // 总金额j小于coins[0] => 不能凑成总金额j
                dp[j] = 0;
            }else{
                // 总金额j大于等于coins[0] => 如果减去当前面额后的总金额j-coins[0]不能正好凑齐,那么总金额j也无法正好凑齐;否则,先凑够j-coins[0]后再加上当前硬币面额即可,总组合数等于dp[j-coins[0]]
                dp[j] = (dp[j-coins[0]] == 0 ? 0 : dp[j-coins[0]]);
            }
        }

        // 递推计算
        for(int i = 1; i < n; i++){
            dp[0] = 1; // 初始化原二维dp中的第一列
            for(int j = 1; j <= amount; j++){
                // if(j < coins[i]){
                //     // 总金额j小于coins[i] => 不能选择coins[i]
                //     dp[j] = dp[j]; // 什么也没做
                // }else{
                //     // 总金额j大于等于coins[i] => 可以选择coins[i],也可以不选择coins[i],两种加起来为总组合数
                //     dp[j] = dp[j] + (dp[j-coins[i]] == 0 ? 0 : dp[j-coins[i]]);
                // }
                if(j >= coins[i]){
                    dp[j] = dp[j] + (dp[j-coins[i]] == 0 ? 0 : dp[j-coins[i]]);
                }
            }
        }

        // 返回结果
        return dp[amount];
    }
}

377. 组合总和 Ⅳ

我觉得这道题实际上是爬楼梯的进阶版,不属于完全背包问题。

因为这道题中是要考虑选择的顺序的,(2, 1, 1)(1, 1, 2)就是两个不同的可行解;而背包问题是不考虑顺序的,在背包问题中认为先选择1还是先选择2都是一样的,比如上一道题518. 零钱兑换 Ⅱ就是一个典型的完全背包问题。

而在爬楼梯问题中,把整个过程看作是一系列的决策过程,而在计算dp[i]时主要考虑的是:在本轮有哪几种选择?

对应到该问题中,就是:将整个过程看作是从nums数组中依次选择数字的过程,每次只能选一个数字,数字可以重复且考虑顺序,问选出的一组数字的总和正好为target的取数方式有几种?

  • dp[i]:从nums中每次选择一个数字(可以重复,考虑顺序),使得总和为i,共有几种选择
  • 在计算dp[i]时:考虑在本轮有哪几种选择?(n种)
    • 共n种选择,对于每一个nums[j],(0<=j<n):如果nums[j] <= i,则可以先选出总和为i-nums[j]的一组数,然后在本轮选择nums[j]
    • 对所有情况求和得到本轮的总选择数
  • 递推公式 d p [ i ] = ∑ j = 0 n − 1 { n u m s [ j ] < = i : d p [ j − d p [ i ] ] n u m s [ j ] > i : 0 } dp[i]=\sum_{j=0}^{n-1} {\begin{Bmatrix} nums[j] <= i: & dp[j-dp[i]]\\ nums[j] > i: & 0 \end{Bmatrix}} dp[i]=j=0n1{nums[j]<=i:nums[j]>i:dp[jdp[i]]0}
  • 计算顺序:在计算dp[i]时可能会用到dp[0...i-1],因此从前向后计算(i = 0 -> target)即可
  • 初始化dp[0]的含义是 “从nums中每次选择一个数字(可以重复,考虑顺序),使得总和为0,共有几种选择”,答案是只有一种选择——什么也不选,因此dp[0] = 1;
class Solution {
    public int combinationSum4(int[] nums, int target) {
        int n = nums.length;

        // dp[i]: 从nums中每次选择一个数字(可以重复,考虑顺序),使得总和为i,共有几种选择
        int[] dp = new int[target+1];

        // 初始化: 总和为0只有1种选择 => 什么也不选
        dp[0] = 1; 
        
        // 递推计算
        for(int i = 1; i <= target; i++){
            dp[i] = 0;
            for(int j = 0; j < n; j++){
                dp[i] += (nums[j] <= i ? dp[i-nums[j]] : 0);
            }
        }
        
        // 返回结果
        return dp[target];
    }
}

322. 零钱兑换

如果用-1来表示不能凑成总金额,会导致dp[]数组中元素的含义不统一,正好题目又要求要求最小值,这样的话在讨论的时候需要对-1的情况进行单独讨论,逻辑有点啰嗦。

代码实现

class Solution {
    public int coinChange(int[] coins, int amount) {
        int n = coins.length;

        // dp[i][j]: 从下标小于i的硬币面额中选择,恰好凑够总金额j所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,则为-1 。
        int[][] dp = new int[n][amount+1];

        // 初始化第一行:用面额为coins[0]的硬币凑成总金额j
        dp[0][0] = 0; // 总金额为0时只有一种组合方式:什么也不选,此时所需的最小硬币数为0
        for(int j = 1; j <= amount; j++){
            if(j < coins[0]){
                // 总金额j小于coins[0] => 不能凑成总金额j
                dp[0][j] = -1;
            }else{
                // 总金额j大于等于coins[0] => 如果减去当前面额后的总金额j-coins[0]不能正好凑齐,那么总金额j也无法正好凑齐;否则,先凑够j-coins[0]后再加上当前硬币面额即可,所需的最少硬币数等于dp[0][j-coins[0]]+1
                dp[0][j] = (dp[0][j-coins[0]] == -1 ? -1 : dp[0][j-coins[0]]+1);
            }
        } 

        // 初始化第一列:凑成总金额0 => 只有一种组合方式:什么也不选,此时所需的最小硬币数为0
        // dp[0][0]已经在前面初始化过了
        for(int i = 1; i < n; i++){
            dp[i][0] = 0;
        }

        // 递推计算
        for(int i = 1; i < n; i++){
            for(int j = 1; j <= amount; j++){
                if(j < coins[i]){
                    // 总金额j小于coins[i] => 不能选择coins[i],问题转换为dp[i-1][j]
                    dp[i][j] = dp[i-1][j];
                }else{
                    // 总金额j大于等于coins[i] => 可以选择coins[i],也可以不选择coins[i],两者取最小
                    // 由于-1具有特殊含义且题目要求是取最小值,此时需要进行分类讨论
                    if(dp[i-1][j] == -1 && dp[i][j-coins[i]] == -1){
                        dp[i][j] = -1; // 如果两种都不能恰好凑成
                    }else if(dp[i-1][j] == -1){
                        // dp[i-1][j] == -1, dp[i][j-coins[i]] != -1
                        dp[i][j] = dp[i][j-coins[i]] + 1;
                    }else if(dp[i][j-coins[i]] == -1){
                        // dp[i-1][j] != -1, dp[i][j-coins[i]] == -1
                        dp[i][j] = dp[i-1][j];
                    }else{
                        dp[i][j] = Math.min(dp[i-1][j], dp[i][j-coins[i]] + 1);
                    }
                }
            }
        }
        
        // 返回结果
        return dp[n-1][amount];
    }
}

反思总结

受到前面377. 组合总和 Ⅳ的启发,我发现其实背包问题在先遍历背包容量再遍历物品的计算顺序下的一维dp优化解法就是按照 “把整个过程看作是一系列的决策过程,而在计算dp[i]时主要考虑的是在本轮如何进行决策” 的思路(这是解决动态规划问题的基本思路——多阶段决策)进行的,按照这一思路直接去思考会直观的多!因此接下来我将尝试按照这种思路来解决问题,第二轮刷题时再统一进行整理。

279. 完全平方数

该问题可以转换为完全背包问题:

  • 共有 ⌊ n ⌋ \left \lfloor \sqrt{n} \right \rfloor n 个物品, 1 ≤ n ≤ ⌊ n ⌋ 1 \leq n \leq \left \lfloor \sqrt{n} \right \rfloor 1nn ,其中第i个物品的重量为 i 2 i^2 i2
  • 背包的最大容量为 n n n
  • 求能使背包恰好装满的最少物品数量

思路二:多阶段决策

dp[i]: 总和为i的完全平方数的最少数量,即背包最大容量为i时使背包恰好装满的最少物品数量。

将这一过程看作一个多阶段决策过程,在每一次决策时,即计算每一个dp[i]时,从所有物品中选择一个装入背包中。
具体来说,计算dp[i]时,即背包最大容量为i时:

  • 有那些选择? 背包能装下的物品下标集合为: { k ∣ 1 ≤ k 2 ≤ i } \{ k | 1\leq k^2 \leq i \} {k∣1k2i}
    • 如果在本轮决策中选择装入物品k,需要先在之前的决策中向背包装入重量为 i − k 2 i-k^2 ik2 的物品。此时使得背包恰好装满时的最少物品数量等于: d p [ i ] = d p [ i − k 2 ] + 1 dp[i] = dp[i-k^2]+1 dp[i]=dp[ik2]+1
  • 如何做决策? 选择装入背包后,使【使得背包恰好装满时的最少物品数量】最少的那一个,其实就是对所有决策结果取最小值
class Solution {
    public int numSquares(int n) {
        // dp[i]: 总和为i的完全平方数的最少数量,即背包最大容量为i时使背包恰好装满的最少物品数量。 
        int[] dp = new int[n+1];

        // 初始化
        dp[0] = 0;

        // 递推计算
        for(int i = 1; i <= n; i++){
            int min = Integer.MAX_VALUE;
            for(int k = 1; k*k <= i; k++){
                min = Math.min(min, dp[i-k*k] + 1);
            }
            dp[i] = min;
        }

        // 返回结果
        return dp[n];
    }
}

139. 单词拆分

该问题问能否用字典worddict中的单词拼出字符串s

可以转换为下面这个问题:

  • 有一个字符串集合worddict
  • 依次从该字符串集合中选出一系列字符串,每个字符串可以选多次(类似于有放回抽取),将这些字符串按顺序拼接
  • 问能否正好拼出字符串s

由于该问题需要考虑顺序,所以只能按照多阶段决策的思路来思考。

dp[i]:问能否用字典worddict中的单词拼出【字符串s的前i个字符组成的前缀字符串】

在每一次决策中,即在计算dp[i]时:

  • 有哪些选择? 可以选择的单词的下标集合: { k ∣ 单词 w o r d d i c t [ k ] 正好是 d p [ i ] 的后缀子字符串 } \{ k | 单词worddict[k]正好是dp[i]的后缀子字符串 \} {k单词worddict[k]正好是dp[i]的后缀子字符串}
    • 如果在本轮决策中选择单词worddict[k],则能否拼出字符串s取决于dp[i-worddict[k].length]
  • 如何做决策?
    • 如果在本轮有某一个选择能够拼出字符串s,本轮的决策结果就为true,其实就是对所有选择的结果做或运算
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        // dp[i]: 问能否用字典worddict中的单词拼出字符串s的前i个字符组成的前缀字符串
        boolean[] dp = new boolean[s.length()+1];

        // 初始化
        dp[0] = true; // 在字典中什么都不选时,正好拼出空字符串

        // 递推计算
        for(int i = 1; i <= s.length(); i++){
            String curStr = s.substring(0, i);
            for(String word : wordDict){
                dp[i] = false;
                if(curStr.endsWith(word) && dp[i-word.length()]){
                    dp[i] = true;
                    break;
                }
            }
        }

        // 返回结果
        return dp[s.length()];
    }
}

碎碎念:事实证明直接用多阶段决策的思路做就OK!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

迪安诊断数智中心战略与PMO负责人徐黎明受邀为第十三届中国PMO大会演讲嘉宾

全国PMO专业人士年度盛会 迪安诊断技术集团股份有限公司数智中心战略与PMO负责人徐黎明先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“软件研发项目管理指标体系建设实践”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; …

Rx(Reactive Extensions)的由来

既然我们已经介绍了响应式编程&#xff0c;现在是时候了解我们的明星了:响应式扩展&#xff0c;通常简称为Rx。微软开发了Reactive扩展库&#xff0c;使其易于处理事件流和数据流。在某种程度上&#xff0c;时变值本身就是一个事件流;每个值更改都是一种类型的事件它会更新依赖…

电流反馈型运放设计要点总结

目录 前言 基本架构 CFB和VFB运算放大器的差异 总结&#xff1a;电流反馈(CFB)与电压反馈(VFB) 前言 最近一个项目用到THS3491&#xff0c;发生了震荡&#xff0c;这是一个电流型反馈运放&#xff0c;借此机会&#xff0c;温故一下&#xff0c;电流运放的相关设计知识 基本架…

JAVA远程调试步骤

1.生成参数 2.复制到启动命令中 3.打jar包运行到远程服务器中 4.开始远程调试

【数据结构与算法 刷题系列】环形链表的约瑟夫问题

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;数据结构与算法刷题系列&#xff08;C语言&#xff09; 目录 一、问题描述 二、解题思路详解 解题思路 解题步骤 三、C语言代码…

NSSCTF | [LitCTF 2023]我Flag呢?

这道题没啥好说的&#xff0c;题目标签为源码泄露&#xff0c;我们直接CtrlU查看网页源码就能在最后找到flag 本题完

Linux---windows 机器和远端的 Linux 机器如何通过 XShell 传输文件

一、关于rzsz 这个工具用于 windows 机器和远端的 Linux 机器通过 Xshell 传输文件. 二、下载rzsz软件 用root输入命令&#xff1a; sudo yum install -y lrzsz下载完成&#xff1a; 三、如何传输 有图形化界面 1、从Windows机器传输给远端Linux机器 ① 直接拖拽 直接将…

从编辑器角度来理解定义和声明

报错,在函数里面(包括int main函数)extern声明会和定义冲突 下面这种写法就很ok 静态变量的反汇编 #include<iostream> using namespace std; extern int c; int ma

Mysql与Java连接----JDBC

前言: 当将Java与MySQL数据库连接时&#xff0c;JDBC&#xff08;Java Database Connectivity&#xff09;是一种重要的技术。JDBC允许Java应用程序通过标准的数据库访问方式与不同的关系型数据库进行通信&#xff0c;其中包括MySQL。通过使用JDBC&#xff0c;Java开发人员可以…

ICode国际青少年编程竞赛- Python-5级训练场-多参数函数

ICode国际青少年编程竞赛- Python-5级训练场-多参数函数 1、 def go(a, b):Spaceship.step(2)Dev.step(a)Spaceship.step(b)Dev.turnRight()Dev.step(b)Dev.turnLeft()Dev.step(-a) Dev.turnLeft() Dev.step(3) Dev.step(-3) go(3, 2) go(6, 1) go(5, 2) go(4, 3)2、 def go(…

processing完整教程

概述&#xff1a;processing在我眼里就是libgdx的高度封装&#xff0c;如果各位会libgdx&#xff0c;学processing应该可以说是无师自通&#xff0c;当然processing是java语言那边的。 processing是什么&#xff1f; 官网是这样解释的&#xff1a;Processing 是一本灵活的软件…

快速判断出485从站设备是否支持MODBUS RTU无线通讯

对于变频器和仪表设备&#xff0c;都支持485串口通讯&#xff0c;那么怎么判断从站设备支持那种协议呢&#xff1f;通常分为两种方式去判断&#xff1a;1.从设备参数参看2.从设备通讯报文查看。本次文章以以台达MH300系列变频器为例。 1.从设备通讯参数查看 使用设备之前一定…

C语言 文件操作

目录 1. 什么是文件&#xff1f;2. 二进制文件和文本文件3. 文件的打开和关闭3.1 流和标准流3.1.1 流3.1.2 标准流 3.2 文件指针3.3 打开、关闭文件3.3.1 fopen - 打开文件3.3.2 fclose - 关闭文件 4. 文件的顺序读写4.1 fgetc - 从文件流获取一个字符4.2 fputc - 将一个字符写…

金融业开源软件应用 评估规范

金融业开源软件应用 评估规范 1 范围 本文件规定了金融机构在应用开源软件时的评估要求&#xff0c;对开源软件的引入、维护和退出提出了实现 要求、评估方法和判定准则。 本文件适用于金融机构对应用的开源软件进行评估。 2 规范性引用文件 下列文件中的内容通过文中的规范…

智能制造数字工厂未来三年规划方案(80页ppt下载)

一、资料介绍 智能制造数字工厂未来三年规划方案是一份全面而深入的战略性文件&#xff0c;旨在指导我们公司在未来三年内实现智能制造领域的跨越式发展。这份80页的PPT资料&#xff0c;以“智能制造、智能制造系统、数字化工厂、数字孪生工厂、智能工厂和数字化车间”为核心关…

Amesim基础篇-热仿真常用模型库-Electrical Basics/Electric Storage

前言 在动力电池仿真中中我们不免会用到该元件库中的模型&#xff0c;该库中包含基本的电路元件与电池模型。具体的相关模型介绍如下&#xff1a; 1 电压源、电流源、功率源、接地 如图&#xff0c;分别为电压源、电流源和功率源&#xff0c;其中功率源在仿真中经常用到&…

《intel开发手册卷3》读书笔记1

1、CPU工作模式 1&#xff09;实模式&#xff1a;8086的寄存器只有16位&#xff0c;我们也习惯于称8086的工作模式为16位模式。后续的CPU为了保持兼容性&#xff0c;在芯片上了电以后&#xff0c;还必须运行于16位模式之下。这种模式还有个正式的名字叫做实模式。在实模式下&am…

Stable Diffusion WebUI 绘画

配置环境介绍​ 目前平台集成了 Stable Diffusion WebUI 的官方镜像&#xff0c;该镜像中整合如下资源&#xff1a; GpuMall智算云 | 省钱、好用、弹性。租GPU就上GpuMall,面向AI开发者的GPU云平台 Stable Diffusion WebUI版本&#xff1a;v1.7.0 Python版本&#xff1a;3.1…

vue2 报错 component name“Index“should always be multi-word

报错原因&#xff1a; 组件名称应该为俩个或俩个以上单词组成的&#xff0c;并且还要是大驼峰命名&#xff0c;例如&#xff1a;MyIndex&#xff0c;MyLogin等 解决方法一&#xff1a; 将组件名称改为俩个或俩个以上单词组成的名称&#xff0c;且为大驼峰命名&#xff0c;例如…

半小时搞懂STM32面经知识——ADC

1.ADC 1.1 ADC是什么&#xff1f; 将连续变量的模拟信号转换为离散变量的数字信号 1.2 ADC的位数&#xff1f;&#xff08;采样精度&#xff09; F1和F4都具有3个ADC&#xff0c;F1可提供21个输入通道&#xff0c;F4可以提供24个输入通道。 F4的ADC支持12位&#xff0c;10位…