C++算法 —— 动态规划(3)多状态

news2024/10/12 10:30:28

文章目录

  • 1、动规思路简介
  • 2、按摩师
  • 3、打家劫舍Ⅱ
  • 4、删除并获得点数
  • 5、粉刷房子
  • 6、买卖股票的最佳时机含冷冻期
  • 7、买卖股票的最佳时机含手续费
  • 8、买卖股票的最佳时机Ⅲ
  • 9、买卖股票的最佳时间Ⅳ


每一种算法都最好看完第一篇再去找要看的博客,因为这样会帮你梳理好思路,看接下来的博客也就更轻松了。当然,我也会尽量在写每一篇时都可以不懂这个算法的人也能边看边理解。

1、动规思路简介

动规的思路有五个步骤,且最好画图来理解细节,不要怕麻烦。当你开始画图,仔细阅读题时,学习中的沉浸感就体验到了。

状态表示
状态转移方程
初始化
填表顺序
返回值

动规一般会先创建一个数组,名字为dp,这个数组也叫dp表。通过一些操作,把dp表填满,其中一个值就是答案。dp数组的每一个元素都表明一种状态,我们的第一步就是先确定状态。

状态的确定可能通过题目要求来得知,可能通过经验 + 题目要求来得知,可能在分析过程中,发现的重复子问题来确定状态。还有别的方法来确定状态,但都大同小异,明白了动规,这些思路也会随之产生。状态的确定就是打算让dp[i]表示什么,这是最重要的一步。状态表示通常用某个位置为结尾或者起点来确定,这点在下面的题解中慢慢领会。

状态转移方程,就是dp[i]等于什么,状态转移方程就是什么。像斐波那契数列,dp[i] = dp[i - 1] + dp[i - 2]。这是最难的一步。一开始,可能状态表示不正确,但不要紧,大胆制定状态,如果没法推出转移方程,没法得到结果,那这个状态表示就是错误的。所以状态表示和状态转移方程是相辅相成的,可以帮你检查自己的思路。

要确定方程,就从最近的一步来划分问题。

初始化,就是要填表,保证其不越界。像第一段所说,动规就是要填表。比如斐波那契数列,如果要填dp[1],那么我们可能需要dp[0]和dp[-1],这就出现越界了,所以为了防止越界,一开始就固定好前两个值,那么第三个值就是前两个值之和,也不会出现越界。初始化的方式不止这一点,有些问题,假使一个位置是由前面2个位置得到的,我们初始化最一开始两个位置,然后写代码,会发现不够高效,这时候就需要设置一个虚拟节点,一维数组的话就是在数组0位置处左边再填一个位置,整个dp数组的元素个数也+1,让原先的dp[0]变为现在的dp[1],二维数组则是要填一列和一行,设置好这一行一列的所有值,原先数组的第一列第一行就可以通过新填的来初始化,这个初始化方法在下面的题解中慢慢领会。

第二种初始化方法的注意事项就是如何初始化虚拟节点的数值来保证填表的结果是正确的,以及新表和旧表的映射关系的维护。

填表顺序。填当前状态的时候,所需要的状态应当已经计算过了。还是斐波那契数列,填dp[4]的时候,dp[3]和dp[2]应当都已经计算好了,那么dp[4]也就出来了,此时的顺序就是从左到右。还有别的顺序,要依据前面的分析来决定。

返回值,要看题目要求。

多状态的题,如果每一天都包含多种含义,比如是什么状态以及还有附加的控制选项,dp表要用多维来表示,如果只是表示什么状态,那就一维数组。多状态的几个状态之间的分析要画图来理解。

2、按摩师

面试题 17.16. 按摩师

在这里插入图片描述

按照动态规划的五步,状态表示,状态转移方程,初始化,填表顺序,返回值。接下来会一步步展开。

我们先分析题,相邻的不能选择,要选择最长时间的那个。所以最长时间我们就能够想到代码中一定比较的部分,max()。相邻不能选择,从1开始走,13579,从第2个开始走,246810,从3个开始走,就和从第1个开始走重复了,所以我们可以把情况分为2种,从第一个位置和第二个位置走。

现在看一看动态规划。状态表示通常是2种,以某个位置结尾,或者某个位置起点。刚才的分析会发现这道题是从左到右走,所以很可能是以某个位置为结尾。那么我们就可以把dp[i]设置为选择到i位置的时候,此时的最长预约时长,为何会是最长预约时长?因为题目中要求求最长时长,不过求的是到达最后时的最长时长,现在我们是要一个个位置走下去,所以就变成了到i位置的时候,dp[i]是最长预约时长。

不过这种选择也有两个子选择,到达i位置时,dp[i]的最长预约时长要不要加上i位置的时长。假设f[i]是加上选择当前位置的值,g[i]是不选当前位置的值。如果要加上当前位置的值,那么前面一个值必定不能选,而它要表示最长预约时长,那么其实这时候f[i] = g[i - 1] + nums[i],前面的不选,就相当于到达前面那个位置,我们不加上当前位置的值,所以就是这个等式;如果到了i位置,我们不选当前这个值,那么前面那个值可选可不选,那么这就又会分出两个情况,i - 1的位置选不选,选的话,其实就相当于到了i - 1位置,我们加上当前位置的值,所以此时g[i] = f[i - 1],不选的话,就相当于到了i - 1位置,我们不加上当前位置的值,所以此时g[i] = g[i - 1],然后这两个值取最大值。现在状态转移方程一共有3个式子。

初始化的时候,dp表如何初始化?这道题不需要加上虚拟节点,因为从dp[0]开始循环,循环里放上方程就可。有些时候加虚拟节点会让代码更简洁,也更无误。根据之前的分析,我们分为两种情况f和g,f[0] = nums[0],g[0] = 0,因为一个选当前节点,一个不选当前节点。

填表顺序是从左到右,两个表一起填。返回值就是两个表的最后一个位置的最大值。

代码书写比较固定,按照分析顺序来就好。当然肯定要先创建dp表。以及这个题需要注意空数组。

    int massage(vector<int>& nums) {
        int n = nums.size();
        if(!n) return 0;
        vector<int> f(n);
        auto g = f;
        f[0] = nums[0], g[0] = 0;
        for(int i = 1; i < n; i++)
        {
            g[i] = max(f[i - 1], g[i - 1]);
            f[i] = g[i - 1] + nums[i];
        }
        return max(f[n - 1], g[n - 1]);
    }

3、打家劫舍Ⅱ

213. 打家劫舍 II

在这里插入图片描述

还有一个打家劫舍Ⅰ,这个题和按摩师是完全一样的代码。也就是说像这样相邻不能选择的题,要想到两个dp表f和g,两个式子f[i] = g[i - 1],g[i] = max(g[i - 1], f[i - 1])。

现在相对于Ⅰ,Ⅱ多了一个条件,第一个房子和最后一个房子是相邻的。也就是选择第一个位置,最后一个位置就不能偷了;从第二个位置开始,最后一个位置就可以偷。如果偷第一个位置,那么其实这相当于从nums[2]到nums[n - 2]位置进行打家劫舍Ⅰ的操作;如果不偷第一个位置,那就从nums[1]到n - 1位置。这就把环形问题换成线性问题。

现在想想代码怎么写。既然要比较两个操作,不如把打家劫舍Ⅰ的操作单独写成一个函数,调用两次,来比较结果,里面调整一下参数。

    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;
        int n = nums.size();
        if(!n) return 0;
        vector<int> f(n);
        auto g = f;
        f[left] = nums[left], g[left] = 0;
        for(int i = left + 1; i <= right; i++)
        {
            g[i] = max(f[i - 1], g[i - 1]);
            f[i] = g[i - 1] + nums[i];
        }
        return max(f[right], g[right]);
    }

4、删除并获得点数

740. 删除并获得点数

在这里插入图片描述

这道题看起来貌似和打家劫舍不同,但我们还是要尽量靠近一些。如果数字都是有序的,比如12345,那么是不是就和不能取相邻元素一样,选择1,2,4,那么3和5都不会被选择,按照这个题来看,它们也确实会被删除,多选一些数字,发现也是一样。但如果是1122222344555怎么办?按照题目以及例子2来看,我们可以将选中的数字多次选中,并且即使该删除的元素没有了,也可以选择,所以这里就可以转换成一个数乘它出现的次数就好了。这个出现的次数字眼就容易想到一个常用的解题办法,创建一个数组arr,下标对应着例子给的数组中的元素,比如下标1就是给定数组中的1,而arr[i]则存储i在给定数组中出现了几次。我们创建好这样的一个数组后,对这个数组做一次打家劫舍就好。

    int deleteAndEarn(vector<int>& nums) {
        const int N = 10001;//因为nums的数最多是1万
        int arr[N] = {0};
        for(auto e : nums)
        {
            arr[e] += e;//如果按照分析,这里应当加1,记录次数,但当写到后面时会发现f[i]需要加arr[i] * i,那么不如就让这一步操作放到这里,直接+=本身的元素,有多少个加多少个,会比用*的写法时间消耗更少
        }
        vector<int> f(N);
        auto g = f;
        //本身g里面都是0,所以不用写g[0] = 0,而f[0]是0 * 次数,所以也是0
        for(int i = 1; i < N; i++)
        {
            g[i] = max(f[i - 1], g[i - 1]);
            f[i] = g[i - 1] + arr[i];
        }
        return max(f[N - 1], g[N - 1]);
    }

5、粉刷房子

LCR 091. 粉刷房子

在这里插入图片描述

也就是说每一个行数都代表一个房间号,每一行的三个元素分别代表红蓝绿色,以及相邻房间颜色得不同。

那么动态规划的思路应该是什么样的?先确定一下状态。我们以i位置时的最小花费。到达i位置时,i位置也有3个颜色,所以我们可以分成dp[i][0],dp[i][1],dp[i][2]。假设选择红色,那么前面就得是蓝或者绿,所以dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + dp[i][0],选择其它颜色也可以按照这个分析推出表达式。这也就是状态转移方程。

初始化的时候,因为填i位置需要前一个位置的值,所以需要初始化第一个值,防止0位置去找-1位置了。对于这样的初始化,有两种方式,一个是我们直接手动填写0位置的值;另一个就是利用虚拟节点,虚拟节点的值不能影响填表以及要注意下标的映射关系改变,比如原表的0位置变成现在的1位置。那么我们需要把虚拟的第一行的3个元素全设置为0才能不影响。

填表顺序是从左往右填表,一次填三个位置,因为是3个颜色,3个分别设置为涂红蓝绿色。返回值是最后一行三个位置的最小值。

    int minCost(vector<vector<int>>& costs) {
        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(min(dp[n][0], dp[n][1]), dp[n][2]);
    }

6、买卖股票的最佳时机含冷冻期

309. 买卖股票的最佳时机含冷冻期

在这里插入图片描述

像这种从左到右填表的题,还是以某个位置为结尾或者起点。我们定状态为第i天结束后最大的利润。每一天结束后都可以表示成三种状态,也就是买入,可交易,冷冻期,买入意思就是当天结束后我有股票,可交易是卖出状态,没有股票,也不在冷冻期,冷冻期状态就是冷冻期。既然这样我们就用二维数组来表示dp表,三种状态对应dp[i][0],dp[i][1],dp[i][2]。dp[i][1]就表示第i天结束之后,处于可交易状态,此时的最大利润。

第i天的状态我们还需要看第i - 1天的状态。dp[i][0]表示第i天结束后处于买入状态。如果前一天结束后是买入状态,也就是有了股票,我们可以当天什么都不做,还是买入状态;如果前一天结束后是可交易状态,那么当天还是可以处于买入状态,买一个股票就行;如果前一天结束后是冷冻期状态,那么意味着我们不能做任何操作,所以当天结束后不能进入买入状态。

如果前一天结束后是冷冻期,那么当天之后冷冻期就过了,就不是了;如果前一天结束后是买入状态,那么当天只要卖出股票,当天之后就是冷冻期了;如果前一天是可交易状态,也就是已经卖出了,那么当天只能买股票或者什么都不做,不能进入冷冻期。

如果前一天结束后是可交易状态,当天结束之后依旧可以是可交易状态,当天什么也不错;如果前一天结束后是买入状态,那么当天结束后最多只能卖出股票,处于冷冻期;如果前一天结束是冷冻期状态,那么当天什么也不做,冷冻期结束,这时候我没有股票,也不在冷冻期,所以就是可交易状态。

经过整体的分析,我们可以这么写状态转移方程,dp[i][0] = max(dp[i - 1][0],dp[i - 1][1] - price[i]),dp[i][1] = max(dp[i - 1][1],dp[i - 1][2]),dp[i][2] = dp[i - 1][0] + price[i]。

因为需要前一天,所以初始化第一天的状态。根据方程来看,dp[0][0] = -price[0],dp[0][1] = 0,dp[0][2] = 0,所以只需要初始化dp[0][0]就行。

填表顺序是从左到右,从上到下。返回值返回dp[n - 1]那一行的最大值,但其实不需要考虑最后一天是买入状态,因为买入的话就需要减去当天的值,所以肯定最小。

    int maxProfit(vector<int>& prices) {
        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]);
    }

7、买卖股票的最佳时机含手续费

714. 买卖股票的最佳时机含手续费

在这里插入图片描述

分析一下这道题,根据题目和例子可以发现,某一天买入后,不一定哪一天卖出,比如买的时候1块钱,卖的时候10块钱,手续费2块,利润就是10 - 1 - 2。

接下来确定状态。以某一天为结尾,也就是某一天结束后能获得的最大利润。题目中只有买入和卖出,所以第i天结束后的状态我们也可以表示成买入和卖出状态,买入状态意味着有股票,卖出状态意味着股票已卖出,手头没有股票,也就是上一个题的可交易状态。

这次我们创建两个数组f和g。g[i]表示第i天结束后,处于卖出状态,此时的最大利润。如果前一天结束后是买入状态,那么当天可以继续处于买入状态,什么也不做;如果前一天是卖出状态,当天也可以处于买入状态,当天再买一个就行。如果前一天是买入状态,当天自然是可以处于卖出状态;如果前一天是卖出状态,当天也可以继续处于卖出状态。

所以状态转移方程就是这样:f[i] = max(f[i - 1],g[i - 1] - prices[i]),g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee)。初始化的时候,因为要用到前一天的状态,所以我们需要考虑第一天的状态,所以f[0]和g[0]都需要考虑,第0天结束后,处于买入状态,那么就需要买这天的股票,如果处于卖出状态,手里没有股票,那么当天只要什么都不做就是这个状态了,因为买入的话,需要至少第二天才能卖出,所以f[0] = -prices[0],g[0] = 0。填表顺序是从左往右,两个表一起填。返回值就是两个表的最后一个值的最大值,但其实因为g的状态,所以最大值应当是g[n - 1]。

    int maxProfit(vector<int>& prices, int fee) {
        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];
    }

8、买卖股票的最佳时机Ⅲ

123. 买卖股票的最佳时机 III

在这里插入图片描述

这道题的一个特点就是最多只有两笔交易,且只能同时拥有一个股票。买入直到卖出的这一天才算一场交易,买入到卖出中间的天数可以不止一天。

现在来确定状态,第i天结束后是什么状态,状态有两个买入和卖出,以及它还有交易次数,所以每天都要表示交易次数和状态,所以就用多维,如果不是这样的,比如只需要表示状态,那就一维。买入和卖出状态写成f和g,f[i][j]表示第i天结束后,完成了j次交易,此时处于买入状态,以及此时的最大利润。

前一天结束后处于买入状态时,当天还可以处于买入状态,交易次数不变;前一天结束后是卖出状态,当天也可以处于买入状态,但如果交易次数已经满2次了,就不能变成买入状态。前一天结束后是卖出状态,可以继续处于卖出状态;前一天结束后是卖出状态,那么得看交易次数,如果满2次,当天就不能再进入买入状态了。根据分析,现在先不管交易次数的限制,没有最多次数的话,最大利润f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]) ,g[i][j] = max(g[i - 1][j] - f[i - 1][j - 1] + prices[i])。对于次数的控制,我们就让j < 3,表示完成012笔交易。

初始化,因为g状态需要i - 1和j - 1,所以需要初始化第一行和第一列,这样的话,我们就换一下方程写法,先让g[i][j] = g[i - 1][j],然后判断j - 1 >= 0的话,也就是说j - 1存在,那么再去判断两值较大者在赋值给g[i][j]。f和g两个状态,初始化第0天的状态,f[0][0]如同上一个题一样,按照它的方程,我们得买一次股票,所以是-prices[i],而g[0][0]则是0,为了不妨碍后续的交易次数,第一行的[1]和[2]位置都是负无穷。但负无穷INT_MIN可能有的编译器对它进行加减操作时就会越界,所以我们用一半值,0x3f3f3f3f来当作最小值,这个值也非常小。

填表顺序是从左到右,从上到下。返回值,因为f状态是买入,也就是当天结束后是买入状态,已经扣去钱买股票了,所以最大值应当在g表。而g表中,最大值应从最后一行中取到,最大利润出现某一次交易之后。

    const int INF = 0x3f3f3f3f;
    int maxProfit(vector<int>& prices) {
        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;
    }

9、买卖股票的最佳时间Ⅳ

188. 买卖股票的最佳时机 IV

在这里插入图片描述

可以发现,它和上一题买卖股票Ⅲ的区别就在于是指定最多2次交易和k次交易。按照上一题的思路,我们定义二维数组,列数就是交易次数 + 1,先不管交易次数的限制,根据方程填表,填完后,进入循环j = 0, j < k + 1,找出g表最后一行的最大值即可。不过这里有个细节问题,k的值其实不需要一定要取这个值,交易次数小于等于k就行,而通过看例子,可以发现,k可以取数组长度一半的值,也可以取k本身的值,有时候0次也可以。

    const int INF = 0x3f3f3f3f;
    int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        k = min(k, n / 2);
        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 + 1; 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 + 1; j++)
        {
            ret = max(ret, g[n - 1][j]);
        }
        return ret;
    }

结束。

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

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

相关文章

正式支持 NVIDIA A100,吞吐量提高 10 倍的Milvus Cloud2.3 使用指南

Milvus 2.3 正式支持 NVIDIA A100! 作为为数不多的支持 GPU 的向量数据库产品,Milvus 2.3 在吞吐量和低延迟方面都带来了显著的变化,尤其是与此前的 CPU 版本相比,不仅吞吐量提高了 10 倍,还能将延迟控制在极低的水准。 不过,正如我前面提到的,鲜有向量数据库支持 GPU,…

必须收藏 | 如何完全卸载ArcGIS

好多小伙伴在卸载ArcGIS过程都遇到了卸载不彻底无法重新安装新版本&#xff0c;卸载残留的注册表找不到等一系列问题&#xff0c;今天小编为大家整理了几个如何完全卸载ArcGIS的方法&#xff0c;希望能够帮到大家&#xff01; #1快捷版 1、开始>控制面板>添加删除程序&…

MR源码解析和join案例

MR源码解析 new Job(): 读取本地文件, xml配置job.start(): 启动线程job的run():线程方法 runTasks(): 传入对应的接口&#xff0c;启动map或者reduceMapTask类的run(): 设置map阶段的参数&#xff0c;初始化任务&#xff0c;创建上下文对象 创建读取器LineRecordReader判断是…

【计算机网络】HTTPS

文章目录 1. HTTPS的概念2. 加密常见的加密方式对称加密非对称加密 3. HTTPS的工作过程的探究方案1 —— 只使用对称加密方案2 —— 只使用 非对称加密方案3 —— 双方都是用非对称加密方案4 —— 非对称加密对称加密中间人攻击引入证书CA认证理解数据签名 方案5 —— 非对称加…

【Redis】1、NoSQL之Redis的配置及优化

关系数据库与非关系数据库 关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记录。 SQL 语句&#xff08;标准数据查询语言&#xff09;就是一种基于关系型数据库的语言&a…

WebGL 绘制矩形

上一节绘制了圆点&#xff0c;调用的绘制方法如下&#xff1a;gl.drawArrays(gl.POINTS, 0, 1); 第一个参数明显是个枚举类型&#xff0c;肯定还有其他值&#xff0c;如下所示&#xff1a; POINTS 可视的点LINES 单独线段LINE_STRIP 线条LINE_LOOP 闭合线条TRIANGLES 单独三…

【Redis7】--1.概述、安装和配置

文章目录 1.Redis概述1.1Redis是什么1.2Redis与MySQL的关系1.3Redis功能1.4Redis优势 2.Redis的安装和配置 1.Redis概述 1.1Redis是什么 Redis全称 远程字典服务器&#xff08;Remote Dictionary Server&#xff09;&#xff0c;它是完全开源的&#xff0c;使用ANSIC语言编写…

算法-26. 删除有序数组中的重复项-⭐

给你一个 升序排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你需要做…

【数据分析】Python:处理缺失值的常见方法

在数据分析和机器学习中&#xff0c;缺失值是一种常见的现象。在实际数据集中&#xff0c;某些变量的某些条目可能没有可用的值。处理缺失值是一个重要的数据预处理步骤。在本文中&#xff0c;我们将介绍如何在 Pandas 中处理缺失值。 我们将探讨以下内容&#xff1a; 什么是缺…

php将数组中的最后一个元素放到第一个

array_unshift($firstStepResult, array_pop($firstStepResult)); 转换之后

spring spring-boot spring-cloud spring-cloud-alibaba之间版本对应关系

spring 版本与 jdk 的对应关系 https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions 从 spring 6.0 开始使用 jdk 17 进行编译 对应的相关 servlet 容器&#xff08;tomcat、undertow、jetty等&#xff09;的 servlet 规范转移到 eclipse&…

算法-27.移除元素-⭐

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面…

MiniDump

一、minidump 模块集成 // .pro QT - guiCONFIG c11 console CONFIG - app_bundle# The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consul…

想兼职?学网络安全,钱赚到你手软

前言 疫情过后&#xff0c;现在的经济形式并不好&#xff0c;所以有很多人想在本职工作以外找一份兼职&#xff0c;亦或者担心自己被裁员&#xff0c;想先学一门技术&#xff0c;从而提高自己的抗风险能力&#xff0c;这没有比网络安全更适合的了。如果有基础的小伙伴完全可以…

01深度学习目标检测引入

目标检测是计算机视觉领域的一个重要任务&#xff0c;旨在从图像或视频中准确地检测和定位特定的目标物体。 一、目标检测问题定义 目标检测是在图片中对可变数量的目标进行查找和分类。 二、目标检测过程中的常见的问题 目标种类和数量问题目标尺度问题外在环境干扰问题 三…

IDEA显示val,var的推断类型的设置

在java中val,var可以减少繁琐的代码量&#xff0c;但是IDEA默认关掉自动类型&#xff0c;导致使用val&#xff0c;var表示的不能一言判别类型&#xff0c;并且不能显示点入查看类型的详情因此需要在idea中设置&#xff0c;能够自动显示推断类型。 步骤1 Setting--->editor…

网络安全(黑客)工具大全

还是一句话&#xff0c;功夫再高&#xff0c;也怕菜刀 首先&#xff0c;恭喜你发现了宝藏。 本文章集成了全网优秀的开源攻防武器项目&#xff0c;包含&#xff1a; 信息收集工具&#xff08;自动化利用工具、资产发现工具、目录扫描工具、子域名收集工具、指纹识别工具、端…

CSP 202112-1 序列查询

答题 这道题不难&#xff0c;但如果直接去实现查询f&#xff08;x&#xff09;的话&#xff0c;算法效率会非常低 我们直接观察样例&#xff0c;15&#xff08;5-2&#xff09;*1&#xff08;8-5&#xff09;*2&#xff08;10-8&#xff09;*3 所以我们可以写出下面程序 #i…

无涯教程-JavaScript - IMSUM函数

描述 IMSUM函数以x yi或x yj文本格式返回两个或多个复数的和。当添加复数时,实数和虚数系数分别相加,即找到两个复数a bi和c di的和的方程为- (a bi)(c in)(a c)(b d)我 语法 IMSUM (inumber1, [inumber2] ...)争论 Argument描述Required/OptionalInumber11 to 25…

css relative 和absolute布局

1、relative和absolute内部的元素都是相对于父容器&#xff0c;若父容器没有指定为relative&#xff0c;则默认为整个文档视图空间&#xff0c;absolute可以重叠元素&#xff0c;relative则不行。relative意味着元素的任意属性如left和right都是相对于其他元素的。absolute则相…