力扣动态规划专题(一)背包理论基础 基础动规题 动规注意点 步骤及C++实现

2023-11-18

动态规划

  1. 动态规划,DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
  2. 与贪心的区别:动态规划中每一个状态一定是由上一个状态推导出来的;贪心没有状态推导,而是从局部直接选最优的。
  3. 动态规划五步骤:
  • 确定dp数组(dp table)以及下标的含义
  • 确定递推公式
  • dp数组如何初始化
  • 确定遍历顺序
  • 举例推导dp数组
  1. 动态规划题目
  • 基础:509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯、62.不同路径、63. 不同路径 II、343. 整数拆分、96.不同的二叉搜索树

  • 背包问题

    • 背包:416. 分割等和子集、1049.最后一块石头的重量II、494.目标和、474.一和零
    • 完全背包:518.零钱兑换II、377.组合总和Ⅳ、70爬楼梯、322.零钱兑换、279.完全平方数、139.单词拆分
    • 多重背包
  • 打家劫舍:198.打家劫舍、213.打家劫舍II、337.打家劫舍III

  • 股票问题:121. 买卖股票的最佳时机(只能买卖一次)、122.买卖股票的最佳时机II(可以买卖多次)、123.买卖股票的最佳时机III(最多买卖两次)、188.买卖股票的最佳时机IV(最多买卖k次)、309.最佳买卖股票时机含冷冻期(买卖多次,卖出一天有冷冻期)、714.买卖股票的最佳时机含手续费(买卖多次,每次有手续费)

  • 子序列问题

    • 不连续子序列:300.最长递增子序列、1143.最长公共子序列、1035.不相交的线
    • 连续子序列:674.最长连续递增子序列、718. 最长重复子数组、53. 最大子序和
    • 编辑距离:392.判断子序列、115.不同的子序列、583. 两个字符串的删除操作、72. 编辑距离
    • 回文:647. 回文子串、516.最长回文子序列

509. 斐波那契数

在这里插入图片描述

五步骤

  1. 确定dp数组以及下标的含义:题目给出了,斐波那契数F(n)就是dp数组,因此第i个数的斐波那契数值F(i)就是dp[i]

  2. 确定递推公式:题目给出了递推公式,F(n) = F(n - 1) + F(n - 2),因此状态转移方程为dp[i] = dp[i - 1] + dp[i - 2]

  3. dp数组如何初始化:题目给出了初始值,F(0) = 0,F(1) = 1,因此dp[0] = 0,dp[1] = 1

  4. 确定遍历顺序:dp[i]是由dp[i - 1]和dp[i - 2]推导而来的,那么遍历顺序是从前往后

  5. 举例推导dp数组:假设i=7,那么dp数组应该是{0, 1, 1, 2, 3, 5, 8, 13}

代码

维护整个数组

class Solution {
public:
    int fib(int n) {
        if(n <= 1) return n;
        vector<int> dp(n+1);//创建dp数组
        dp[0] = 0;//dp数组初始值
        dp[1] = 1;//dp数组初始值
        //开始从前往后遍历
        for(int i=2; i<=n; i++)
        {
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
    }
};

改进,只维护两个数值

class Solution {
public:
    //改进,只维护两个数值,不是整个数组
    int fib(int n)
    {
        if(n <= 1) return n;
        int dp[2];//数组 仅维护两个数值
        dp[0] = 0;
        dp[1] = 1;
        int sum = 0;
        for(int i=2; i<=n; i++)
        {
            sum = dp[0] + dp[1];//中间状态记录
            dp[0] = dp[1];//当前dp[0] = 上一个dp[1]
            dp[1] = sum;//当前dp[1] = 上一个的 dp[0]+dp[1]
        }
        return dp[1];
    }
};

70. 爬楼梯

在这里插入图片描述

五步骤

  1. 确定dp数组以及下标的含义:dp[i],爬到第i层楼梯,有dp[i]种方法

  2. 确定递推公式:
    题目中说每次可以爬1或2个台阶,那么有两个方向可以得到dp[i]
    如果只爬一个台阶,就从i-1层楼梯上一个台阶。即dp[i - 1],上i-1层楼梯,有dp[i - 1]种方法,再上一个台阶就是dp[i]。
    如果只爬两个台阶,就从i-2层楼梯上两个台阶。即dp[i - 2],上i-2层楼梯,有dp[i - 2]种方法,再上两个台阶就是dp[i]。
    所以dp[i]就是dp[i - 1]与dp[i - 2]之和,即dp[i] = dp[i - 1] + dp[i - 2]

  3. dp数组如何初始化:
    不考虑dp[0]如何初始化,只初始化dp[1] = 1,dp[2] = 2,然后从i = 3开始递推

  4. 确定遍历顺序:dp[i]是由dp[i - 1]和dp[i - 2]推导而来的,那么遍历顺序是从前往后

  5. 举例推导dp数组:假设i=10,那么dp数组应该是{1,2,3,5,8,13,21,44,65,109}

  6. 和斐波那契数唯一的区别就是没有dp[0]

代码

  • 维护整个数组
class Solution {
public:
    int climbStairs(int n) {
        //2.维护整个数组
        if(n <= 1) return n;
        vector<int> dp(n+1);
        dp[1] = 1;
        dp[2] = 2;
        for(int i=3; i<=n; i++)
        {
            dp[i] = dp[i-1] + dp[i-2];
        }
        return dp[n];
    }
};
  • 维护两个数值
class Solution {
public:
    int climbStairs(int n) {
        //1.只维护两个数值
        if(n <= 1) return n;
        int dp[3];
        dp[1] = 1;
        dp[2] = 2;
        int sum = 0;
        for(int i=3; i<=n; i++)
        {
            sum = dp[1] + dp[2];
            dp[1] = dp[2];
            dp[2] = sum;
        }
        return dp[2];
    }
};

746. 使用最小花费爬楼梯

五步骤

  1. 确定dp数组以及下标的含义:dp[i],爬到第i层楼梯所花费最少的体力

  2. 确定递推公式:

  • 和70.爬楼梯相同的是,每次可以爬1或2个台阶,也就是有两个方向可以推导出dp[i],即dp[i] = dp[i - 1] + dp[i - 2]。
  • 和70.爬楼梯不同的是,每次爬楼需要支付费用:
    从i-1层楼梯dp[i - 1] 再上 一个台阶到dp[i],需要花费dp[i - 1] + cost[i - 1]
    从i-1层楼梯dp[i - 2] 再上 两个台阶到dp[i],需要花费dp[i - 2] + cost[i - 2]
  • 因此到达dp[i]层楼梯,一共需要花费dp[i - 1] + cost[i - 1] + dp[i - 2] + cost[i - 2]。但题目中要求是最小花费,不是所有方法的总费用。到达dp[i]层楼梯,哪种方法下费用最少就用哪种方法爬楼,即dp[i] = min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 2])
  1. dp数组如何初始化:
    注意题目中描述的你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯,也就是说,到第0个或者第1个台阶不需要花钱,但是从第0个或者第1个台阶需要花钱,可以看题目给的例子。因此初始化dp[10] = 0,dp[1] = 0

  2. 确定遍历顺序:dp[i]是由dp[i - 1]和dp[i - 2]推导而来的,那么遍历顺序是从前往后

  3. 举例推导dp数组:假设使用题目的示例2,cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1],则有

cost[i] 1 100 1 1 1 100 1 1 100 1
下标i 0 1 2 3 4 5 6 7 8 9
dp[i] 0 0 1 2 2 3 3 4 4 5

代码

  • 维护整个数组
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        //1.维护整个数组
        vector<int> dp(cost.size()+1);
        dp[0] = 0;
        dp[1] = 0;
        for(int i=2; i<=cost.size(); i++)
        {
            dp[i] = min(dp[i-1]+cost[i-1], dp[i-2]+cost[i-2]);
        }
        return dp[cost.size()];
    }
};
  • 维护两个数值 使用数组
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp[2];
        dp[0] = dp[1] = 0;
        int finalcost = 0;
        for(int i=2; i<=cost.size(); i++)
        {
            finalcost = min(dp[0]+cost[i-2], dp[1]+cost[i-1]);
            dp[0] = dp[1];
            dp[1] = finalcost;
        }
        return dp[1];
    }
};
  • 维护两个数值 不用数组
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int dp0 = 0, dp1 = 0;
        int dpi;
        for(int i=2; i<=cost.size(); i++)
        {
            dpi = min(dp1+cost[i-1], dp0+cost[i-2]);
            dp0 = dp1;
            dp1 = dpi;
        }
        return dp1;
    }
};

扩展

如果按照第一步花钱,最后一步不花钱。也就是在第0个或者第1个台阶需要花钱,但是到达顶楼的时候不需要花钱

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        //4.如果按照 第一步是花钱的,最后一步不花钱
        vector<int> dp(cost.size()+1);
        dp[0] = cost[0];
        dp[1] = cost[1];
        for(int i = 2; i<cost.size(); i++)//顶楼不花钱 不取等号
        {
            dp[i] = min(dp[i-2], dp[i-1]) + cost[i];
        }
        //最后一步不花钱的话,取倒数第一步和倒数第二步的最小值
        return min(dp[cost.size()-1], dp[cost.size()-2]);
    }
};

62. 不同路径

在这里插入图片描述
在这里插入图片描述

动态规划

五步骤

  1. 确定dp数组以及下标的含义:表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径

  2. 确定递推公式:和爬楼梯的题目类似,每次可以选择向下或者向右走,也就是有两个方向可以推导出dp[i][j],即dp[i][j] = dp[i - 1][j] + dp[i][j-1]

  3. dp数组如何初始化:从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i][0]一定都是1,那么dp[0][j]也是1

  4. 确定遍历顺序:dp[i][j]是由dp[i - 1][j]和dp[i][j-1]推导而来的,也就是从上或者左边推导而来,那么遍历顺序是从左到右

  5. 举例推导dp数组:假设m=5,n=6,则有

1 1 1 1 1 1
1 2 3 4 5 6
1 3 6 10 15 21
1 4 10 20 35 56
1 5 15 35 70 126

代码

  • 两个数组,逐行逐列遍历,有m*n个位置,每个位置记录对应的路径数,保存每个位置的结果
1 1 1 1 1 1
1 2 3 4 5 6
1 3 6 10 15 21
1 4 10 20 35 56
1 5 15 35 70 126
class Solution {
public:
    int uniquePaths(int m, int n) {
        //1.两个数组
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for(int i=0; i<m; i++) dp[i][0] = 1;//初始化
        for(int j=0; j<n; j++) dp[0][j] = 1;//初始化
        for(int i=1; i<m; i++)
        {
            for(int j=1; j<n; j++)
            {
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
};
  • 一个数组时,逐行遍历,只有n或m个位置,每个位置记录的是到达该位置的总路径数,数组只保存i=n的结果,每次都更新,滚动数组
i=1 1 1 1 1 1 1
i=2 1 2 3 4 5 6
i=3 1 3 6 10 15 21
i=4 1 4 10 20 35 56
i=5 1 5 15 35 70 126
class Solution {
public:
    int uniquePaths(int m, int n) {
        //2.一个数组
        vector<int> dp(n, 0);
        for(int i=0; i<n; i++) dp[i] = 1;
        for(int j=1; j<m; j++)
        {
            for(int i=1; i<n; i++)
            {
                dp[i] += dp[i-1];
            }
        }
        return dp[n-1];
    }
};

数论

在这里插入图片描述
在这m + n - 2 步中,一定有 m - 1 步是要向下走的,不用管什么时候向下走。那么可以转化为组合问题,给m + n - 2个不同的数,随便取m - 1个数,有几种取法:

C m + n − 2 m − 1 C_{m+n-2}^{m-1} Cm+n2m1 == C m − 1 + n − 1 m − 1 C_{m-1+n-1}^{m-1} Cm1+n1m1 == C m + n − 2 n − 1 C_{m+n-2}^{n-1} Cm+n2n1 == ( m + n − 2 ) ! ( n − 1 ) ! × ( m − 1 ) ! {(m+n-2)!\over (n-1)!×(m-1)!} (n1)!×(m1)!(m+n2)! == ( m − 1 + n − 2 ) × . . . × m × ( m − 1 ) × . . . × 2 × 1 ( n − 1 ) × . . . × 2 × 1 × ( m − 1 ) × . . . × 2 × 1 {(m-1+n-2)×...×m×(m-1)×...×2×1\over (n-1)×...×2×1×(m-1)×...×2×1} (n1)×...×2×1×(m1)×...×2×1(m1+n2)×...×m×(m1)×...×2×1 == ( m − 1 + n − 2 ) × . . . × m 1 {(m-1+n-2)×...×m\over 1} 1(m1+n2)×...×m

注意最后结果是返回m×…×(m-1+n-2),也就是分母除以分子的结果,还要注意分子溢出的情况,不能直接模拟公式计算。

class Solution {
public:
    int uniquePaths(int m, int n) {
        //3.数论 组合问题
        long long numerator = 1; // 分子
        int denominator = m - 1; // 分母
        int count = m-1;//相当于分子分母相消m-1项
        int t = m+n-2;//分子项
        while(count--)
        {
            numerator *= (t--);
            //分母不为0;numerator%denominator==0 表示找到相消项
            while(denominator!=0 && numerator%denominator==0)
            {
                numerator /= denominator;//更新结果
                denominator--;//分母项
            }
        }
        return numerator;
    }
};

63. 不同路径 II

在这里插入图片描述
在这里插入图片描述

五步骤

  1. 确定dp数组以及下标的含义:表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径

  2. 确定递推公式:

  • 和62题一样,每次可以选择向下或者向右走,有两个方向可以推导出dp[i][j],即dp[i][j] = dp[i - 1][j] + dp[i][j-1]
  • 和62题不一样的是,遇到障碍就保持初始状态,相当于重新出发
  1. dp数组如何初始化:
  • 从(0, 0)的位置到(i, 0)的路径只有一条,所以dp[i][0]一定都是1,那么dp[0][j]也是1
  • 如果(i, 0)位置有障碍,该位置及其后面的路都没有办法走,就要重新出发。即遇到障碍后,dp[i][0]=0,对于(0, j)的位置也是同样的处理
  1. 确定遍历顺序:dp[i][j]是由dp[i - 1][j]和dp[i][j-1]推导而来的,也就是从上或者左边推导而来,那么遍历顺序是从左到右
  2. 举例推导dp数组,略

代码

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        //1.动态数组 两个数组
        int n = obstacleGrid[0].size();
        int m = obstacleGrid.size();
        //起点或者终点有障碍物 无路可走
        if(obstacleGrid[0][0]==1 || obstacleGrid[m-1][n-1]==1) return 0;
        //初始化dp数组
        vector<vector<int>> dp(m, vector<int>(n, 0));
        for(int i=0; i<m && obstacleGrid[i][0] == 0; i++) dp[i][0] = 1;
        for(int j=0; j<n && obstacleGrid[0][j] == 0; j++) dp[0][j] = 1;
        //更新dp数组
        for(int i=1; i<m; i++)
        {
            for(int j=1; j<n; j++)
            {
                if(obstacleGrid[i][j] == 1) continue;
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
};
  • 一个数组时,逐行遍历,只有n或m个位置,每个位置记录的是到达该位置的总路径数,数组只保存i=n的结果,每次都更新,滚动数组
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        //2.动态数组 一个数组
        if(obstacleGrid[0][0] == 1) return 0;
        int n = obstacleGrid[0].size();
        vector<int> dp(n, 0);
        //初始化
        for(int j=0; j<n; j++)
        {
            if(obstacleGrid[0][j] == 1) dp[j] = 0;//遇到障碍物
            else if(j==0) dp[j] = 1;
            else dp[j] = dp[j-1];
        }
        //更新dp数组
        for(int i=1; i<obstacleGrid.size(); i++)
        {
            for(int j=0; j<n; j++)//注意这里从0开始
            {
                if(obstacleGrid[i][j] == 1) dp[j] = 0;
                else if(j!=0) dp[j] += dp[j-1];
            }
        }
        return dp[n-1];
    }
};

343. 整数拆分

在这里插入图片描述

五步骤

  1. 确定dp数组以及下标的含义:分拆数字i,可以得到的最大乘积为dp[i]

  2. 确定递推公式:

  • 有两个方向推导,j * (i - j)
  • j×(i - j),直接把整数拆分为两个数相乘
  • j×dp[i - j],相当于是拆分(i - j),拆分成两个以及两个以上的个数相乘
  • 如果定义dp[i - j]×dp[j] 也是默认将一个数强制拆成4份以及4份以上了
  • 递推公式:dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j});,在递推公式推导的过程中,每次计算dp[i],取最大值
  1. dp数组如何初始化:拆分0和拆分1的最大乘积没有意义。从dp[i]的定义来说,拆分数字2,得到的最大乘积是1,初始化dp[2] = 1。

  2. 确定遍历顺序:

  • dp[i]是由dp[i - 1]推导而来的,那么遍历顺序是从左到右
  • j的结束条件是 j < i - 1,也可以是 j < i,但是后者重复计算。j=1时,拆分j与i-1;j = i - 1时,拆分i-1与1,重复计算。
  • i从3开始,那么dp[i - j]=dp[2],可以通过初始化值求出来
  1. 拆分一个数n使其乘积最大,那么一定是拆分成m个数值相近的因子相乘的乘积最大。10 拆成 3 * 3 * 4和拆成2 * 5,前者的乘积更大。虽然无法确定m,但m一定大于等于2,也就是意味着拆成两个相同的因子的乘积有可能是最大值。那么遍历j时,只需要遍历到 n/2 就可以了,但乘积一定不是最大值。

  2. 举例推导dp数组,n为10 时

i 3 4 5 6 7 8 9 10
j 1~2 1~3 1~4 1~5 1~6 1~7 1~8 1~9
i-j 2~1 3~1 4~1 5~1 6~1 7~1 8~1 9~1
dp[i-j] dp[2] dp[3] dp[2] dp[4] dp[3] dp[2] dp[5] dp[4] dp[3] dp[2] dp[6] dp[5] dp[4] dp[3] dp[2] dp[7] dp[6] dp[5] dp[4] dp[3] dp[2] dp[8] dp[7] dp[6] dp[5] dp[4] dp[3] dp[2] dp[9] dp[8] dp[7] dp[6] dp[5] dp[4] dp[3] dp[2]
dp[i] dp[3] dp[4] dp[5] dp[6] dp[7] dp[8] dp[9] dp[10]

代码

class Solution {
public:
    int integerBreak(int n) {
    //1.动态规划
    vector<int> dp(n+1);
    dp[2] = 1;
    for(int i=3; i<=n; i++)//dp[1] dp[0]没有意义
    {
        //内层for循环有三种写法
        //1.for(int j=1; j<i; j++)
        //2.for(int j=1; j<i-1; j++)
        for(int j=1; j<=i/2; j++)
        {
            dp[i] = max(dp[i], max(j*(i-j), j*dp[i-j]));
        }
    }
    return dp[n];
    }
};

96.不同的二叉搜索树

在这里插入图片描述

五步骤

  1. 确定dp数组以及下标的含义:1到i为节点组成的二叉搜索树的个数为dp[i],i个不同元素节点组成的二叉搜索树的个数

  2. 确定递推公式:
    在这里插入图片描述

  • 1为头结点时,相当于n=2的两棵树加了值为1的头结点,3为头结点时,亦同理。2为头结点时,布局同n=1的树。

  • dp[3]可以通过dp[1] 和 dp[2] 推导,即元素1为头结点搜索树的数量 + 元 素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量:

    • 元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量,dp[2] * dp[0];
    • 元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量,dp[1] * dp[1];
    • 元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量,dp[0] * dp[2];
  • dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2]

  • dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量],j相当于是头结点的元素,从1遍历到i为止。

  • dp[i] += dp[j - 1] * dp[i - j]; ,j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量

  1. dp数组如何初始化:空节点也是一棵二叉树,也是一棵二叉搜索树,为了避免乘法为0,初始化dp[0]=1。

  2. 确定遍历顺序:dp[i] += dp[j - 1] * dp[i - j]可以看出,节点数为i的状态是依靠 i之前节点数的状态,用j来遍历i中每一个数作为头结点的状态

  3. 拆分一个数n使其乘积最大,那么一定是拆分成m个数值相近的因子相乘的乘积最大。10 拆成 3 * 3 * 4和拆成2 * 5,前者的乘积更大。虽然无法确定m,但m一定大于等于2,也就是意味着拆成两个相同的因子的乘积有可能是最大值。那么遍历j时,只需要遍历到 n/2 就可以了,但乘积一定不是最大值。

  4. 举例推导dp数组
    在这里插入图片描述

代码

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n+1);
        dp[0] = 1;//初始化空树的状态为1,避免乘法为0
        //更新dp数组
        for(int i=1; i<=n; i++)//以i为头结点的树
        {
            //符合条件的有多少棵树 用j从1-i遍历
            for(int j=1; j<=i; j++)
            {
                //对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加
                //一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-j
                //dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]
                dp[i] += dp[j-1] * dp[i-j];
            }
        }
        return dp[n];
    }
};

注意点:

  • 从509、70、746题来看,如果不到最后一层for循环的结束条件不取等号,到的话要取等号。例如,爬楼梯是,最后一层花钱或者要到顶楼的话,for循环的结束条件都要取等号。
  • 509、70、746题三个题大同小异,要注意dp数组初始化时要结合dp数组的定义,例如70、746题dp数组初始化的区别。
  • 不同路径可以用动态规划和数论的方法,数论注意分子溢出,注意遇到障碍物的处理方法
  • 不同的二叉搜索树,好难想到
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

力扣动态规划专题(一)背包理论基础 基础动规题 动规注意点 步骤及C++实现 的相关文章

随机推荐

  • mvn install的时候出现的Please refer to dump files (if any exist) [date].dump

    出现问题 Please refer to Users wanghaohao Downloads workspace BigDataOss target surefire reports for the individual test res
  • 【网络编程】学习成果day1(理论)

    1 联合体实现判断大小端存储 linux linux study NETbc cat homework1 c include
  • kali2.0 Metasploit连接postgres数据库

    sudo u postgres createuser demo P 创建用户demo sudo u postgres psql 进入数据库 postgres password demo 设置密码 sudo u postgres create
  • CK-GW06-E00与CODESYS TCP通信

    CK GW06 E00与CODESYS TCP通信 CK GW06 E00是一款支持标准工业通讯协议Modbus TCP的网关控制器 方便用户集成 到 PLC 等控制系统中 本控制器提供了网络 POE 供电和直流电源供电两种方式 确保用 户
  • 计数排序动漫与代码

    前言 从复杂度较高的 冒泡排序 选择排序 插入排序 不包含二分插入排序 这些时间复杂度为 O N 2 的排序 再到归并排序 快速排序 堆排序 这些时间复杂度为 O nlogn 的排序 还有么有更快的 答有 它来了它来了 它带着一波动画共计1
  • 解决keil中 点击setting 程序中断问题

    自己写了一个LED常亮的程序 入门嘛 但是程序在下载后 点击 debug setting 在软件识别J link后 程序是成功的 但是LED不亮了 下面是解决方法 记住把2标记处的对勾去掉就可以了 这个功能是 在你连接完成时自动在Reset
  • linux怎样重启网卡,linux系统重启网卡的方法步骤详解

    在实际工作中 经常会遇到Linux系统进行重启网卡的操作 接下来是小编为大家收集的linux系统重启网卡方法 希望能帮到大家 linux系统重启网卡方法 一 service network restart 1 首先用CRT工具连接到Linu
  • GPT-4更新了哪些功能-GPT-4 Plus申请流程

    GPT 4相比GPT3有哪些更新 以下是对GPT 4的优势的详解 1 更强大的语言生成能力 GPT 4将有望比其前身 GPT 3模型生成更精准 更准确 更自然的文本 同时在更短的时间内完成 2 更高的文本理解能力 随着深度学习的技术进步 G
  • Ubuntu操作系统学习笔记之文件系统挂载管理

    概念 在磁盘或分区上创建好的文件系统 需要挂载到一个目录才能使用 windows和Mac会进行自动挂载 即文件系统一创建好就自动挂载到系统上 挂载到windows上的文件系统称之为 C盘 D盘 E盘 等 Linux需要手动进行挂载操作 或者
  • Typora图片路径问题之相对路径前没有点斜杠

    一 背景 在使用typora时 复制一张图片显示的路径前面没有点斜杠 二 遇到的问题 如图所示 原因 typora偏低版本还没有自动在前面加点斜杠的功能 三 解决方法 升级最新版本 效果如图 1 4 3 新增功能 添加选项以在为图像生成相对
  • lua 源码阅读 1.1 -> 2.1

    lua 1 1 阅读1 hash c 中 a 对建立的 Hash array 用 listhead 链式结构来管理 新增lua hashcollector 用来做 Hash 的回收处理 ps 1 0 用的是个 512 的数组 用一个少一个
  • 关于Qt::BlockingQueuedConnection非死锁的程序卡住

    Qt BlockingQueuedConnection如果sender和receiver运行于同一线程 会报错 Qt Dead lock detected while activating a BlockingQueuedConnectio
  • 网络化,元宇宙的门槛

    如果森林中的一棵树倒下 但周围没有人听到 那它是否会发出声音 这一思想实验可以追溯到数百年前 这个实验之所以经久不衰 部分原因是它很有趣 而它之所以很有趣 是因为它耐人寻味并且融人了哲思 人们通常认为 上面这个问题最初是由主观唯心主义哲学家
  • 【自动化测试】如何在jenkins中搭建allure

    相信大家在做自动化测试过程中 都会用到自动化测试环境 目前最常见的就是通过容器化方式部署自动化测试环境 但对于一些测试小白 不是很会搭建持续集成环境 特别是从0 1的过程 需要自行搭建很多依赖环境 今天就给大家介绍一下如何在jenkins搭
  • 小程序开发-Now you can provide attr "wx:key" for a "wx:for" to improve performance

    当我们开发微信小程序用到 wx for 时可能会在控制台报 Now you can provide attr wx key for a wx for to improve performance 警告 不要慌张这只是警告不妨碍运行 消除警告
  • html 常用知识点

    段落 h3 登黄鹤楼 h3 p 白日依山尽 p p 黄河入海流 p 跳转页面 a href http www daidu com target blank title 欢迎登陆 a href 链接的地址 target 打开页面的方式 tit
  • do while(0)的妙用

    do while 0 的妙用 do while 0 就如同一个花括号 具有独立的作用域 花括号所表示的符合语句是一个整体 do while 语句同样是一个整体 同样可以在if 等条件语句后直接使用 但是后所不同的是 do while 作为循
  • Mac 解决 command not found: mysql

    首先检查下面的目录中是否有mysql命令 cd usr local mysql bin 如果有的话 使用 echo export PATH PATH usr local mysql bin gt gt zshrc 即可解决 参考链接 htt
  • Ubuntu关闭无用服务

    linux的各大发行版 都有些不必要的服务被默认开启了 针对ubuntu 我们可以采用选择性关闭的方法加速起动 提高系统性能 这里我们安装一个软件 sudo apt get install sysv rc conf 然后这样起动 sudo
  • 力扣动态规划专题(一)背包理论基础 基础动规题 动规注意点 步骤及C++实现

    文章目录 动态规划 509 斐波那契数 五步骤 代码 70 爬楼梯 五步骤 代码 746 使用最小花费爬楼梯 五步骤 代码 扩展 62 不同路径 动态规划 数论 63 不同路径 II 五步骤 代码 343 整数拆分 五步骤 代码 96 不同