专题三:简单多状态 dp 问题

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:了解什么是记忆化搜索,并且掌握记忆化搜索算法。

> 毒鸡汤:有些事情,总是不明白,所以我不会坚持。早安!

> 专栏选自:动态规划算法_დ旧言~的博客-CSDN博客

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

一、算法讲解

动态规划(Dynamic Programming)算法的核心思想是:将大问题划分为小问题进行解决,从而一步步获取最优解的处理算法:

  • 动态规划算法与分治算法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。
  • 与分治法不同的是,适合于用动态规划求解的问题,经分解得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上)。

【Tips】动态规划算法解决问题的分类

  • 计数:有多少种方式走到右下角 / 有多少种方法选出k个数使得和是 sum。
  • 求最大值/最小值:从左上角走到右下角路径的最大数字和最长上升子序列长度。
  • 求存在性:取石子游戏,先手是否必胜 / 能不能取出 k 个数字使得和是 sum。

【Tips】动态规划dp算法一般步骤

  • 确定状态表示(dp[ i ] 的含义是什么,来源:1、题目要求;2、经验+题目要求;3、分析问题时发现重复子问题)
  • 状态转移方程(可求得 dp[ i ] 的数学公式,来源:题目要求+状态表示)
  • 初始化(dp 表中特别的初始值,保证填 dp 表时不会越界,来源:题目要求+状态表示)
  • 填表顺序(根据状态转移方程修改 dp[ i ] 的方式,来源:题目要求+状态表示)
  • 返回值(题目求解的结果,来源:题目要求+状态表示)

二、算法习题

2.1 第一题

题目链接:面试题 17.16. 按摩师 - 力扣(LeetCode)

题目描述:

算法思路:

1. 状态表⽰:

dp[i] 表⽰:选择到 i 位置时,此时的最⻓预约时⻓。

但是我们这个题在 i 位置的时候,会⾯临「选择」或者「不选择」两种抉择,所依赖的状态需要
细分:

  • f[i] 表⽰:选择到 i 位置时, nums[i] 必选,此时的最⻓预约时⻓;
  • g[i] 表⽰:选择到 i 位置时, nums[i] 不选,此时的最⻓预约时⻓。

2. 状态转移⽅程:

对于 f[i] :

如果 nums[i] 必选,那么我们仅需知道 i - 1 位置在不选的情况下的最⻓预约时⻓,然后加上nums[i] 即可,因此 f[i] = g[i - 1] + nums[i] 。

对于 g[i] :

如果 nums[i] 不选,那么 i - 1 位置上选或者不选都可以。因此,我们需要知道 i - 1 位置上选或

3. 初始化:

这道题的初始化⽐较简单,因此⽆需加辅助节点,仅需初始化 f[0] = nums[0], g[0] = 0 即可。

4. 填表顺序:

根据「状态转移⽅程」得「从左往右,两个表⼀起填」。

5. 返回值:

根据「状态表⽰」,应该返回 max(f[n - 1], g[n - 1]) 。

代码呈现:

class Solution {
public:
    int massage(vector<int>& nums) {
        // 1. 创建⼀个 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回值
        int n = nums.size();
        if (n == 0)
            return 0; // 处理边界条件
        vector<int> f(n);
        auto g = f;
        f[0] = nums[0];
        for (int i = 1; i < n; i++) 
        {
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[n - 1], g[n - 1]);
    }
};

2.2 第二题

题目链接:213. 打家劫舍 II - 力扣(LeetCode)

题目描述:

算法思路:

上⼀个问题是⼀个「单排」的模式,这⼀个问题是⼀个「环形」的模式,也就是⾸尾是相连的。但是我们可以将「环形」问题转化为「两个单排」问题:

  1. 偷第⼀个房屋时的最⼤⾦额 x ,此时不能偷最后⼀个房⼦,因此就是偷 [0, n - 2] 区间的房⼦;
  2. 不偷第⼀个房屋时的最⼤⾦额 y ,此时可以偷最后⼀个房⼦,因此就是偷 [1, n - 1] 区间的房⼦;

两种情况下的「最⼤值」,就是最终的结果。因此,问题就转化成求「两次单排结果的最⼤值」。

代码呈现:

class Solution {
public:
    int rob(vector<int>& nums) 
    {
        int n = nums.size();
        // 两种情况下的最⼤值
        return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));
    }
    int rob1(vector<int>& nums, int left, int right) 
    {
        if (left > right)
            return 0;
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回结果
        int n = nums.size();
        vector<int> f(n);
        auto g = f;
        f[left] = nums[left]; // 初始化
        for (int i = left + 1; i <= right; i++) 
        {
            f[i] = g[i - 1] + nums[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        return max(f[right], g[right]);
    }
};

2.3 第三题

题目链接:740. 删除并获得点数 - 力扣(LeetCode)

题目描述:

算法思路:

我们注意到题⽬描述,选择 x 数字的时候, x - 1 与 x + 1 是不能被选择的。像不像「打家劫舍」问题中,选择 i 位置的⾦额之后,就不能选择 i - 1 位置以及 i + 1 位置的⾦额呢~

因此,我们可以创建⼀个⼤⼩为 10001 (根据题⽬的数据范围)的 hash 数组,将 nums 数组中每⼀个元素 x ,累加到 hash 数组下标为 x 的位置处,然后在 hash 数组上来⼀次「打家劫舍」可。

代码呈现:

class Solution {
public:
    int deleteAndEarn(vector<int>& nums) 
    {
        const int N = 10001;
        // 1. 预处理
        int arr[N] = {0};
        for (auto x : nums)
            arr[x] += x;
        // 2. 在 arr 数组上,做⼀次 “打家劫舍” 问题
        // 创建 dp 表
        vector<int> f(N);
        auto g = f;
        // 填表
        for (int i = 1; i < N; i++) 
        {
            f[i] = g[i - 1] + arr[i];
            g[i] = max(f[i - 1], g[i - 1]);
        }
        // 返回结果
        return max(f[N - 1], g[N - 1]);
    }
};

2.4 第四题

题目链接:LCR 091. 粉刷房子 - 力扣(LeetCode)

题目描述:

算法思路:

1. 状态表⽰:

我们这个题在 i 位置的时候,会⾯临「红」「蓝」「绿」三种抉择,所依赖的状态需要细分:

  • dp[i][0] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「红⾊」,此时的最⼩花费;
  • dp[i][1] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「蓝⾊」,此时的最⼩花费;
  • dp[i][2] 表⽰:粉刷到 i 位置的时候,最后⼀个位置粉刷上「绿⾊」,此时的最⼩花费。

2. 状态转移⽅程:

对于 dp[i][0] :

如果第 i 个位置粉刷上「红⾊」,那么 i - 1 位置上可以是「蓝⾊」或者「绿⾊」。因此我们需要知道粉刷到 i - 1 位置上的时候,粉刷上「蓝⾊」或者「绿⾊」的最⼩花费,然后加上 i 位置的花费即可。于是状态转移⽅程为: dp[i][0] = min(dp[i - 1][1], dp[i- 1][2]) + costs[i - 1][0] ;

同理,我们可以推导出另外两个状态转移⽅程为:

  • dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1] ;
  • dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2] 。

3. 初始化:

可以在最前⾯加上⼀个「辅助结点」,帮助我们初始化。使⽤这种技巧要注意两个点:

  1.  辅助结点⾥⾯的值要「保证后续填表是正确的」;
  2. 「下标的映射关系」。
  3. 在本题中,添加⼀个节点,并且初始化为 0 即可。 

4. 填表顺序:

根据「状态转移⽅程」得「从左往右,三个表⼀起填」。

5. 返回值

根据「状态表⽰」,应该返回最后⼀个位置粉刷上三种颜⾊情况下的最⼩值,因此需要返回:
min(dp[n][0], min(dp[n][1], dp[n][2])) 。

代码呈现:

class Solution {
public:
    int minCost(vector<vector<int>>& costs) 
    {
        // dp[i][j] 第i个房⼦刷成第j种颜⾊最⼩花费
        int n = costs.size();
        vector<vector<int>> dp(n + 1, vector<int>(3));
        for (int i = 1; i <= n; i++) 
        {
            dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0];
            dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1];
            dp[i][2] = min(dp[i - 1][1], dp[i - 1][0]) + costs[i - 1][2];
        }
        return min(dp[n][0], min(dp[n][1], dp[n][2]));
    }
};

2.5 第五题

题目链接:309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)

题目描述:

  

算法思路:

1. 状态表⽰:

由于有「买⼊」「可交易」「冷冻期」三个状态,因此我们可以选择⽤三个数组,其中:

  • dp[i][0] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;
  • dp[i][1] 表⽰:第 i 天结束后,处于「可交易」状态,此时的最⼤利润;
  • dp[i][2] 表⽰:第 i 天结束后,处于「冷冻期」状态,此时的最⼤利润。

2. 状态转移⽅程:

  1. 处于「买⼊」状态的时候,我们现在有股票,此时不能买股票,只能继续持有股票,或者卖出股票;
  2. 处于「卖出」状态的时候:

        • 如果「在冷冻期」,不能买⼊;
        • 如果「不在冷冻期」,才能买⼊。
对于 dp[i][0] ,我们有「两种情况」能到达这个状态:

  1. 在 i - 1 天持有股票,此时最⼤收益应该和 i - 1 天的保持⼀致: dp[i - 1][0] ;
  2. 在 i 天买⼊股票,那我们应该选择 i - 1 天不在冷冻期的时候买⼊,由于买⼊需要花钱,所以此时最⼤收益为: dp[i - 1][1] - prices[i]

两种情况应取最⼤值,因此: dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] -prices[i]) 。

对于 dp[i][1] ,我们有「两种情况」能到达这个状态:

  1. 在 i - 1 天的时候,已经处于冷冻期,然后啥也不⼲到第 i 天,此时对应的状态为:dp[i - 1][2] ;
  2. 在 i - 1 天的时候,⼿上没有股票,也不在冷冻期,但是依旧啥也不⼲到第 i 天,此时对应的状态为 dp[i - 1][1] ;

两种情况应取最⼤值,因此: dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]) 。

对于 dp[1][i] ,我们只有「⼀种情况」能到达这个状态:

  • 在 i - 1 天的时候,卖出股票。

因此对应的状态转移为: dp[i][2] = dp[i - 1][0] + prices[i] 。

代码呈现:

class Solution {
public:
    int maxProfit(vector<int>& prices) 
    {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回结果
        int n = prices.size();
        vector<vector<int>> dp(n, vector<int>(3));
        dp[0][0] = -prices[0];
        for (int i = 1; i < n; i++) 
        {
            dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
            dp[i][1] = max(dp[i - 1][1], dp[i - 1][2]);
            dp[i][2] = dp[i - 1][0] + prices[i];
        }
        return max(dp[n - 1][1], dp[n - 1][2]);
    }
};

2.6 第六题

题目链接:714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)

题目描述:

算法思路:

1. 状态表⽰:

由于有「买⼊」「可交易」两个状态,因此我们可以选择⽤两个数组,其中:

  • f[i] 表⽰:第 i 天结束后,处于「买⼊」状态,此时的最⼤利润;
  • g[i] 表⽰:第 i 天结束后,处于「卖出」状态,此时的最⼤利润。

2. 状态转移⽅程:

我们选择在「卖出」的时候,⽀付这个⼿续费,那么在「买⼊」的时候,就不⽤再考虑⼿续费的问
题。
对于 f[i] ,我们有两种情况能到达这个状态:

  1. 在 i - 1 天「持有」股票,第 i 天啥也不⼲。此时最⼤收益为 f[i - 1] ;
  2. 在 i - 1 天的时候「没有」股票,在第 i 天买⼊股票。此时最⼤收益为 g[i - 1]- prices[i]) ;
  3. 两种情况下应该取最⼤值,因此 f[i] = max(f[i - 1], g[i - 1] -prices[i]) 。

对于 g[i] ,我们也有两种情况能够到达这个状态:

  1. 在 i - 1 天「持有」股票,但是在第 i 天将股票卖出。此时最⼤收益为: f[i - 1 + prices[i] - fee) ,记得⼿续费;
  2. 在 i - 1 天「没有」股票,然后第 i 天啥也不⼲。此时最⼤收益为: g[i - 1] ;
  3. 两种情况下应该取最⼤值,因此 g[i] = max(g[i - 1], f[i - 1] + prices[i]- fee) 。

3. 初始化:

由于需要⽤到前⾯的状态,因此需要初始化第⼀个位置。

  • 对于 f[0] ,此时处于「买⼊」状态,因此 f[0] = -prices[0] ;
  • 对于 g[0] ,此时处于「没有股票」状态,啥也不⼲即可获得最⼤收益,因此 g[0] = 0 。

4. 填表顺序:

毫⽆疑问是「从左往右」,但是两个表需要⼀起填。

5. 返回值:

应该返回「卖出」状态下,最后⼀天的最⼤值收益: g[n - 1] 。

代码呈现:

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) 
    {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回结果
        int n = prices.size();
        vector<int> f(n);
        auto g = f;
        f[0] = -prices[0];
        for (int i = 1; i < n; i++) 
        {
            f[i] = max(f[i - 1], g[i - 1] - prices[i]);
            g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee);
        }
        return g[n - 1];
    }
};

2.7 第七题

题目链接:123. 买卖股票的最佳时机 III - 力扣(LeetCode)

题目描述:

算法思路:

1. 状态表⽰:

由于有「买⼊」「可交易」两个状态,因此我们可以选择⽤两个数组。但是这道题⾥⾯还有交易次数的限制,因此我们还需要再加上⼀维,⽤来表⽰交易次数。其中:

  • f[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「买⼊」状态,此时的最⼤利润;
  • g[i][j] 表⽰:第 i 天结束后,完成了 j 次交易,处于「卖出」状态,此时的最⼤利润。

2. 状态转移⽅程:

对于 f[i][j] ,我们有两种情况到这个状态:

  1. 在 i - 1 天的时候,交易了 j 次,处于「买⼊」状态,第 i 天啥也不⼲即可。此时最⼤利润为: f[i - 1][j] ;
  2. 在 i - 1 天的时候,交易了 j 次,处于「卖出」状态,第 i 天的时候把股票买了。此时的最⼤利润为: g[i - 1][j] - prices[i] 。
  3. 综上,我们要的是「最⼤利润」,因此是两者的最⼤值: f[i][j] = max(f[i - 1][j],g[i - 1][j] - prices[i]) 。

对于 g[i][j] ,我们也有两种情况可以到达这个状态:

  1. 在 i - 1 天的时候,交易了 j 次,处于「卖出」状态,第 i 天啥也不⼲即可。此时的最⼤利润为: g[i - 1][j] ;
  2. 在 i - 1 天的时候,交易了 j - 1 次,处于「买⼊」状态,第 i 天把股票卖了,然后就完成了 j ⽐交易。此时的最⼤利润为: f[i - 1][j - 1] + prices[i] 。但是这个状态不⼀定存在,要先判断⼀下。
  3. 综上,我们要的是最⼤利润,因此状态转移⽅程为:g[i][j] = g[i - 1][j]; if(j >= 1) g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);

3. 初始化:

由于需要⽤到 i = 0 时的状态,因此我们初始化第⼀⾏即可。

  • 当处于第 0 天的时候,只能处于「买⼊过⼀次」的状态,此时的收益为 -prices[0] ,因此 f[0][0] = - prices[0] 。
  • 为了取 max 的时候,⼀些不存在的状态「起不到⼲扰」的作⽤,我们统统将它们初始化为 -INF (⽤ INT_MIN 在计算过程中会有「溢出」的⻛险,这⾥ INF 折半取0x3f3f3f3f ,⾜够⼩即可)

4. 填表顺序:

从「上往下填」每⼀⾏,每⼀⾏「从左往右」,两个表「⼀起填」。

5. 返回值:

返回处于「卖出状态」的最⼤值,但是我们也「不知道是交易了⼏次」,因此返回 g 表最后⼀⾏的最⼤值。

代码呈现:

class Solution {
public:
    const int INF = 0x3f3f3f3f;
    int maxProfit(vector<int>& prices) 
    {
        // 1. 创建 dp 表
        // 2. 初始化
        // 3. 填表
        // 4. 返回结果
        int n = prices.size();
        vector<vector<int>> f(n, vector<int>(3, -INF));
        auto g = f;
        f[0][0] = -prices[0], g[0][0] = 0;
        for (int i = 1; i < n; i++) 
        {
            for (int j = 0; j < 3; j++) 
            {
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                if (j >= 1) // 如果该状态存在
                    g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        // 找到最后⼀⾏的最⼤值
        int ret = 0;
        for (int j = 0; j < 3; j++)
            ret = max(ret, g[n - 1][j]);
        return ret;
    }
};

2.8 第八题

题目链接:188. 买卖股票的最佳时机 IV - 力扣(LeetCode)

题目描述:

算法思路:

1. 状态表⽰:

为了更加清晰的区分「买⼊」和「卖出」,我们换成「有股票」和「⽆股票」两个状态。

  • f[i][j] 表⽰:第 i 天结束后,完成了 j 笔交易,此时处于「有股票」状态的最⼤收益;
  • g[i][j] 表⽰:第 i 天结束后,完成了 j 笔交易,此时处于「⽆股票」状态的最⼤收益。

2. 状态转移⽅程:

对于 f [i][j] ,我们也有两种情况能在第 i 天结束之后,完成 j 笔交易,此时⼿⾥「有股
票」的状态:

  1. 在 i - 1 天的时候,⼿⾥「有股票」,并且交易了 j 次。在第 i 天的时候,啥也不⼲。此时的收益为 f[i - 1][j] ;
  2.  在 i - 1 天的时候,⼿⾥「没有股票」,并且交易了 j 次。在第 i 天的时候,买了股票。那么 i 天结束之后,我们就有股票了。此时的收益为 g[i - 1][j] -prices[i] ;
  3. 上述两种情况,我们需要的是「最⼤值」,因此 f 的状态转移⽅程为:f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i])

对于 g [i][j] ,我们有下⾯两种情况能在第 i 天结束之后,完成 j 笔交易,此时⼿⾥「没有
股票」的状态:

  1. 在 i - 1 天的时候,⼿⾥「没有股票」,并且交易了 j 次。在第 i 天的时候,啥也不⼲。此时的收益为 g[i - 1][j] ;
  2. 在 i - 1 天的时候,⼿⾥「有股票」,并且交易了 j - 1 次。在第 i 天的时候,把股票卖了。那么 i 天结束之后,我们就交易了 j 次。此时的收益为 f[i - 1][j -1] + prices[i] ;
  3. 上述两种情况,我们需要的是「最⼤值」,因此 g 的状态转移⽅程为:g[i][j] = max(g[i - 1][j], f[i - 1][j - 1] + prices[i])

3. 初始化:

由于需要⽤到 i = 0 时的状态,因此我们初始化第⼀⾏即可。

  • 当处于第 0 天的时候,只能处于「买⼊过⼀次」的状态,此时的收益为 -prices[0] ,因此 f[0][0] = - prices[0] 。
  • 为了取 max 的时候,⼀些不存在的状态「起不到⼲扰」的作⽤,我们统统将它们初始化为 -INF (⽤ INT_MIN 在计算过程中会有「溢出」的⻛险,这⾥ INF 折半取0x3f3f3f3f ,⾜够⼩即可)

4. 填表顺序:

从上往下填每⼀⾏,每⼀⾏从左往右,两个表⼀起填。

5. 返回值:

返回处于卖出状态的最⼤值,但是我们也不知道是交易了⼏次,因此返回 g 表最后⼀⾏的最⼤值。

代码呈现:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) 
    {
        const int INF = 0x3f3f3f3f;
        // 处理⼀个细节问题
        int n = prices.size();
        k = min(k, n / 2);
        // 创建 dp 表
        // 初始化
        // 填表
        // 返回值
        vector<vector<int>> f(n, vector<int>(k + 1, -INF));
        auto g = f;
        f[0][0] = -prices[0], g[0][0] = 0;
        for (int i = 1; i < n; i++) 
        {
            for (int j = 0; j <= k; j++) 
            {
                f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]);
                g[i][j] = g[i - 1][j];
                if (j >= 1) // 如果状态存在
                    g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]);
            }
        }
        int ret = 0;
        for (int j = 0; j <= k; j++)
            ret = max(ret, g[n - 1][j]);
        return ret;
    }
};

三、结束语 

今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

​​ 

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

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

相关文章

海外组网之优解:外贸 SD-WAN 跨境组网专线赋能企业全球互联

在全球化商业进程中&#xff0c;众多企业积极拓展海外业务&#xff0c;设立国内外分支&#xff0c;然而分支组网难题也随之而来。外贸 SD-WAN 跨境专线成为解决此类问题的优选方案&#xff0c;为企业提供高效稳定的网络连接。 一、SD-WAN 跨境组网专线优势 &#xff08;一&…

【机器人】轨迹规划 之 spline 规划

在轨迹规划中&#xff0c;使用 spline &#xff08;通常是指通过样条曲线进行轨迹规划&#xff09;可以实现平滑、连续的路径。以下是使用样条&#xff08;如B样条、三次样条插值&#xff09;的具体方法和步骤&#xff0c;结合一个简单的例子说明&#xff1a; 示例场景&#xf…

Python生成对抗神经网络GAN预测股票及LSTMs、ARIMA对比分析ETF金融时间序列可视化

全文链接&#xff1a;https://tecdat.cn/?p38528 本文聚焦于利用生成对抗网络&#xff08;GANs&#xff09;进行金融时间序列的概率预测。介绍了一种新颖的基于经济学驱动的生成器损失函数&#xff0c;使 GANs 更适用于分类任务并置于监督学习环境中&#xff0c;能给出价格回…

【渗透测试】信息收集二

其他信息收集 在渗透测试中&#xff0c;历史漏洞信息收集是一项重要的工作&#xff0c;以下是相关介绍&#xff1a; 历史漏洞信息收集的重要性 提高效率&#xff1a;通过收集目标系统或应用程序的历史漏洞信息&#xff0c;可以快速定位可能存在的安全问题&#xff0c;避免重复…

【泛微系统】流程发起次数报表

流程发起次数报表 应用场景: 查询所有发起过业务流程的员工的信息,可作为绩效考核、系统使用情况等依据; 如何使用该SQL生成系统在线报表,实时查询最新的发起数据? 1、数据库创建视图,并定义一个视图名称如;view_test1 2、系统后台建模引擎-表单-右键创建一个虚拟表单…

k8s中用filebeat文件如何收集不同service的日志

以下是一个详细的从在 Kubernetes 集群中部署 Filebeat&#xff0c;到实现按web-oper、web-api微服务分离日志并存储到不同索引的完整方案&#xff1a; 理解需求&#xff1a;按服务分离日志索引 在 Kubernetes 集群中&#xff0c;有web-oper和web-api两种微服务&#xff0c;希…

【LeetCode】2406、将区间分为最少组数

【LeetCode】2406、将区间分为最少组数 文章目录 一、数据结构-堆、贪心1.1 数据结构-堆、贪心1.2 多语言解法 二、扫描线2.1 扫描线 一、数据结构-堆、贪心 1.1 数据结构-堆、贪心 题目已知一些区间, 需要尽量合并, 使 组 最少. 可以用图解画一下 因为尽量合并, 为了紧凑, …

【现代服务端架构】传统服务器 对比 Serverless

在现代开发中&#xff0c;选择合适的架构是至关重要的。两种非常常见的架构模式分别是 传统服务器架构 和 Serverless。它们各有优缺点&#xff0c;适合不同的应用场景。今天&#xff0c;我就带大家一起对比这两种架构&#xff0c;看看它们的差异&#xff0c;并且帮助你选择最适…

数据结构——栈的模拟实现

大家好&#xff0c;今天我要介绍一下数据结构中的一个经典结构——栈。 一&#xff1a;栈的介绍 与顺序表和单链表不同的是&#xff1a; 顺序表和单链表都可以在头部和尾部插入和删除数据&#xff0c;但是栈的结构就锁死了&#xff08;栈的底部是堵死的&#xff09;栈只能从…

Harmony编译与打包

1、hvigor编译脚本文件 hvigorconfig.ts:位于项目根目录,默认不存在(可以自行创建),执行的时机较早,可用于在hvigor生命周期刚开始时操作某些数据。hvigorfile.ts:项目根目录和每个模块目录下都有,在此文件中可以注册插件、任务以及生命周期hook等操作(类似Android的b…

健康的玉米叶病害数据集,玉米识别数据集,对原始图片进行yolov,coco,voc格式标注

健康的玉米叶病害数据集 对原始图片进行yolov&#xff0c;coco&#xff0c;voc格式标注&#xff0c;可识别玉米叶子是否健康 数据集分割 训练组100&#xff05; 442图片 有效集&#xff05; 0图片 测试集&#xff05; 0图片 预处理 自动定向&#xff1a;…

MobileLLM开发安卓AI的体验

MobileLLM是一个在安卓端跑的大语言模型&#xff0c;关键它还有调动api的能力 https://github.com/facebookresearch/MobileLLM 项目地址是这个。 看了下&#xff0c;似乎还是中国人团队 article{liu2024mobilellm, title{MobileLLM: Optimizing Sub-billion Parameter Langua…

IIS部署程序https是访问出现403或ERR_HTTP2_PROTOCOL_ERROR

一、说明 在windows server 2016中的IIS程序池里部署一套系统&#xff0c;通过https访问站点&#xff0c;同时考虑到安全问题以及防攻击等行为&#xff0c;就用上了WAF云盾功能&#xff0c;能有效的抵挡部分攻击&#xff0c;加强网站的安全性和健壮性。 应用系统一直能够正常…

2024小迪安全信息收集第三课

目录 一、Web应用-架构分析-WAF&蜜罐识别 二、Web应用-架构分析-框架组件指纹识别 #Web架构 开源CMS 前端技术 开发语言 框架组件 Web服务器 应用服务器 数据库类型 操作系统信息 应用服务信息 CDN信息 WAF信息 蜜罐信息 其他组件信息 #指纹识别 #WAF识别…

计算机网络技术基础:3.计算机网络的拓扑结构

网络拓扑结构是指用传输媒体互连各种设备的物理布局&#xff0c;即用什么方式把网络中的计算机等设备连接起来。将工作站、服务站等网络设备抽象为点&#xff0c;称为“节点”&#xff1b;将通信线路抽象为线&#xff0c;称为“链路”。由节点和链路构成的抽象结构就是网络拓扑…

【数据结构】基数排序的原理及实现

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在准备26考研 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章…

opencv-python的简单练习

一、编程题 读取一张彩色图像并将其转换为灰度图。 import cv2# 读取彩色图像 image_path path_to_your_image.jpg # 替换为你的图像路径 color_image cv2.imread(image_path)# 检查图像是否成功加载 if color_image is None:print("图像加载失败&#xff0c;请检查图…

Python鼠标轨迹算法(游戏防检测)

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

【USB-HID】“自动化键盘“ - 模拟键盘输入

目录 【USB-HID】"自动化键盘" - 模拟键盘输入1. 前言2. 模拟键盘2.1 STM32CubeMX 配置2.2 修改代码配置2.3 发送按键信息 3. 接收主机Setup数据3.1 获取PC下发的数据 4. 总结 【USB-HID】“自动化键盘” - 模拟键盘输入 1. 前言 对于模拟键盘的实现&#xff0c;网…

图-遍历(DFS+BFS)

图-遍历 1.简介2.深度优先遍历dfs3.广度优先遍历bfs4.具体问题4.1 岛屿的最大面积4.2 岛屿的数量 5.总结 1.简介 图是数据结构中的另一种数据结构&#xff0c;通常用来表示多对多的关系。在 C 中&#xff0c;图通常可以通过邻接表或邻接矩阵表示。 例如&#xff1a; 2.深度优…