【动态规划专栏】-- 01 背包问题 -- 动态规划经典题型

news2025/4/13 4:54:32

目录

背包问题概述

01 背包问题

01背包⭐⭐ 

【算法原理】

第一问

第二问

C++ 算法代码

复杂度分析

【空间优化 - 滚动数组】

C++ 算法代码

复杂度分析

分割等和子集⭐⭐

【算法原理】 

对于类01背包问题

C++ 算法代码 

【空间优化 - 滚动数组】 

C++ 算法代码

目标和⭐⭐

【算法原理】 

C++ 算法代码 

【空间优化 - 滚动数组】 

C++ 算法代码

最后一块石头的重量Ⅱ⭐⭐⭐ 

【算法原理】 

C++ 算法代码 

【空间优化 - 滚动数组】 

C++ 算法代码


背包问题概述

        背包问题 (Knapsack problem) 是⼀种组合优化的 NP完全问题

        问题可以描述为:给定⼀组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高

根据物品的个数,分为如下几类:
  • 01 背包问题:每个物品只有⼀个。
  • 完全背包问题:每个物品有无限多个。
  • 多重背包问题:每件物品最多有 si 个。
  • 混合背包问题:每个物品会有上⾯三种情况......。
  • 分组背包问题:物品有 n 组,每组物品⾥有若干个,每组里最多选⼀个物品。
其中上述分类里面,根据背包是否装满,又分为两类:
  • 不一定装满背包
  • 背包一定装满
优化方案:
  • 空间优化 - 滚动数组
  • 单调队列优化
  • 贪心优化
根据限定条件的个数,⼜分为两类:
  • 限定条件只有⼀个:比如体积 -> 普通的背包问题
  • 限定条件有两个:比如体积 + 重量 -> ⼆维费用背包问题
根据不同的问法,又分为很多类:
  • 输出方案
  • 求方案总数
  • 最优方案
  • 方案可行性
        因此,背包问题种类非常繁多,题型非常丰富,难度也是非常难以捉摸。但是,尽管种类非常多,都是从 01 背包问题演化过来。所以: ⼀定要把 01 背包问题学好

01 背包问题


01背包⭐⭐ 

【模板】01背包_牛客题霸_牛客网 (nowcoder.com)


【算法原理】

第一问

#:状态表示:

        根据「经验 + 题目要求」我们继续尝试「用 i 位置为结尾」结合「题目要求」得到,dp[ i ]表示:从前 i 个物品中挑选,总体积不超过 j ,所有选法中,能挑选出来的最大价值。(也看出来划了杠,看似很完美,但其实是错的),因为一个背包是有体积的,有体积为关键的背包仅仅看第几个物品是没用的。

        我们需要将背包的体积也需要包含进去,d[ i ][ j ]表示:从前 i 个物品中挑选,总体积不超过 j ,所有选法中,能挑选出来的最大价值

#:状态转移方程:

线性 dp 状态转移方程分析方式,⼀般都是根据「最后⼀步」的状况,来分情况讨论:
  • 不选第 i 个物品:相当于就是去前 i - 1 个物品中挑选,并且总体积不超过 j,此时 dp[ i ][ j ] = dp[ i - 1 ][ j ]。
  • 选择第 i 个物品:那么我就只能去前 i - 1 个物品中,挑选总体积不超过 j - v[ i ] 的物品。此时 dp[ i ][ j ] = dp[ i - 1 ][ j - v[ i ] ] + w[ i ]。但是这种状态不⼀定存在,因此需要特判⼀下。

        综上,状态转移方程为:dp[ i ][ j ] = max(dp[ i - 1 ][ j ], dp[ i - 1 ][ j - v[ i ] ] + w[ i ])

#:初始化:

       我们多加⼀行,方便我们的初始化,此时仅需将第⼀行初始化为 0 即可。因为什么也不选,也能满足体积不小于 j 的情况,此时的价值为 0

#:填表顺序:

        根据「状态转移方程」,我们仅需「从上往下」填表即可。

#:返回值:

        根据「状态表示」,返回 dp[n][V]  

第二问

#:状态表示:

        根据「经验 + 题目要求」我们继续尝试「用 i 位置为结尾」结合「题目要求」得到,dp[ i ][ j ] 表示:从前 i 个物品中挑选,总体积「正好」等于 j ,所有的选法中,能挑选出来的最⼤价值。

#:状态转移方程:

  • 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 ] ] 表示的情况是否存在,也就是 dp[ i - 1 ][ j - v[ i ] ] != -1

#:初始化:

我们多加一行,方便我们的初始化:

  • 第一个格子为 0 ,因为正好能凑齐体积为 0 的背包。
  • 但是第一行后面的格子都是 -1 ,因为没有物品,无法满足体积大于 0 的情况。

#:填表顺序:

        根据「状态转移方程」,我们仅需「从上往下」填表即可。

#:返回值:

        由于最后可能凑不成体积为 V 的情况,因此返回之前需要「特判」⼀下。


C++ 算法代码

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <memory.h>
using namespace std;

int main()
{
    int n = 0, V = 0;
    cin >> n >> V;
    vector<pair<int, int>> val;
    val.reserve(n + 1);
    val.push_back(make_pair(0, 0));
    for (int i = n; i > 0 ; i--) {
        int v = 0, w = 0;
        cin >> v >> w;
        val.push_back(make_pair(v, w));
    }

    // 背包不满的时候的最大价值
    // 1、创建dp表
    vector<vector<int>> dp(n + 1, vector<int>(V + 1, 0));

    // 2、初始化 

    // 3、填表
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = 1; j < V + 1; j++)
        {
            dp[i][j] = dp[i - 1][j];

            if (j >= val[i].first)
                dp[i][j] = max(dp[i][j],
                               dp[i - 1][j - val[i].first] + val[i].second);
        }
    }

    // 4、返回值
    cout << dp[n][V] << endl;


    // 背包满的时候的最大价值
    // 1、创建dp表
    fill(dp.begin(), dp.end(), vector<int>(V + 1, 0));

    // 2、初始化
    for (int i = 1; i < V + 1; i++)
        dp[0][i] = -1;

    // 3、填表
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = 1; j < V + 1; j++)
        {
            dp[i][j] = dp[i - 1][j];

            if (j >= val[i].first && dp[i - 1][j - val[i].first] != -1)
                dp[i][j] = max(dp[i][j],
                               dp[i - 1][j - val[i].first] + val[i].second);
        }
    }

    // 4、返回值
    cout << (dp[n][V] == -1 ? 0 : dp[n][V]) << endl;

    return 0;
}

复杂度分析

  • 时间复杂度:O(n*m),两层for循环。
  • 空间复杂度:O(n*m)

【空间优化 - 滚动数组】

        首先根据状态方程:dp[ i ][ j ] = max(dp[ i - 1 ][ j ], dp[ i - 1 ][ j - v[ i ] ] + w[ i ]),可以发现只是和两行有关,所以我们就可以首先用两个一维数组解决。

        由前一行的两个元素推出需求的位置。根据此我们可以推出一个一维数组解决,只不过有一个问题

        所以,防止在未使用的时候出现被更改的情况,建议以从右往左的顺序进行。

C++ 算法代码

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <memory.h>
using namespace std;

int main()
{
    int n = 0, V = 0;
    cin >> n >> V;
    vector<pair<int, int>> val;
    val.reserve(n + 1);
    val.push_back(make_pair(0, 0));
    for (int i = n; i > 0 ; i--) {
        int v = 0, w = 0;
        cin >> v >> w;
        val.push_back(make_pair(v, w));
    }

    // 背包不满的时候的最大价值
    // 1、创建dp表
    vector<int> dp(V + 1, 0);

    // 2、初始化 

    // 3、填表
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = V; j >= val[i].first; j--)
        {
            if (j >= val[i].first)
                dp[j] = max(dp[j], dp[j - val[i].first] + val[i].second);
        }
    }

    // 4、返回值
    cout << dp[V] << endl;


    // 背包满的时候的最大价值
    // 1、创建dp表
    fill(dp.begin(), dp.end(), -1);

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

    // 3、填表
    for (int i = 1; i < n + 1; i++)
    {
        for (int j = V; j >= val[i].first; j--)
        {
            if (dp[j - val[i].first] != -1)
                dp[j] = max(dp[j], dp[j - val[i].first] + val[i].second);
        }
    }

    // 4、返回值
    cout << (dp[V] == -1 ? 0 : dp[V]) << endl;
    return 0;
}

优化细节:

        因为如果出现 j >= val[i].first;那么 max(dp[j], dp[j - val[i].first] + val[i].second);没有执行的意义会越界。

        未优化的时候因为需要 dp[ i ][ j ] = dp[i - 1][ j ];所以必须将第二层循环迭代完成,但是此处由于已经优化为一维数组,所以无需继续迭代。

复杂度分析

  • 时间复杂度:O(n*m),两个两层for循环。
  • 空间复杂度:O(n)

分割等和子集⭐⭐

416. 分割等和子集 - 力扣(LeetCode)


【算法原理】 

对于类01背包问题

  • 转换:对应的题目提供的要素,时常是需要一个转换过程的(十分重要)

        重点在于每一个元素的不选的问题,也就是01背包关键的地方。

  • 01背包 "模板":不是说里面的东西都是照抄过来的,而是里面的分析思路 "模板",根据题目进行一定方式的微调
        将其转化为「熟悉」的题型,如果数组能够被分成两个相同元素之和相同的子集,那么原数组必须有下面几个性质:
  • 所有元素之和应该是⼀个偶数。
  • 挑选⼀些数,这些数的总和应该等于数组总和的⼀半。

        根据前两个性质,我们可以提前判断数组能够被划分,根据最后⼀个性质,我们发现问题就转化成了「01 背包」的模型:

  • 数组中的元素只能选择⼀次。
  • 每个元素面临被选择或者不被选择的处境。
  • 选出来的元素总和要等于所有元素总和的⼀半。

        其中,数组内的元素就是物品,总和就是背包。 那么我们就可以⽤背包模型的分析方式,来处理这道题。 「不要背」01背包的状态转移方程,我们要记住的是分析问题的模式,用这种分析问题的模式来解决问题。

 #:状态表示:

        根据「经验 + 题目要求」我们继续尝试「用 i 位置为结尾」结合「题目要求」得到,dp[ i ][ j ] 表示在前 i 个元素中选择,所有的选法中,能否凑成总和为 j 这个数。

#:状态转移方程:

根据「最后⼀个位置」的元素,结合题目的要求,分情况讨论:

  • 不选择 nums[ i ] :那么我们是否能够凑成总和为 j ,就要看在前 i - 1 个元素中选,能否凑成总和为 j 。根据状态表示:dp[ i ][ j ] = dp[ i - 1 ][ j ]
  • 选择 nums[ i ] :这种情况下是有前提条件的,此时的 nums[ i ] 应该是小于等于 j 因为如果这个元素都比要凑成的总和大,选择它就没有意义呀。那么我们是否能够凑成总和j ,就要看在前 i - 1 个元素中选,能否凑成总和为 j - nums[ i ] 。根据状态表示dp[ i ][ j ] = dp[ i - 1 ][ j - nums[ i ]]

        综上所述,两种情况下只要有⼀种能够凑成总和为 j ,那么这个状态就是 true 。因此,状态转移方程为:dp[ i ][ j ] = dp[ i - 1 ][ j ];if(nums[ i ] <= j)  dp[ i ][ j ] = dp[ i ][ j ] || dp[ i - 1 ][ j - nums[ i ]]

#:初始化:

        由于添加了一行一列,因此我们可以先把第一行初始化,第一行表示不选择任何元素,要凑成目标和 j 。只有当目标和为 0 的时候才能做到,因此第一行仅需初始化第⼀个元素 dp[0][0] = true。一列中不放任何元素就可以了,于是第一列全为true。

#:填表顺序:

        根据「状态转移方程」,我们需要「从上往下」填写每一行,每一行的顺序是「无所谓的」。

#:返回值:

        根据「状态表示」,返回 dp[ n ][ aim ] 的值。其中 n 表示数组的大小, aim 表示要凑的目标和。


C++ 算法代码 

class Solution {
public:
    bool canPartition(vector<int>& nums) {

        int n = nums.size();
        int sum = 0;
        for(auto val : nums)
            sum += val;

        if(sum % 2) return false;

        // 1、创建dp表
        vector<vector<bool>> dp(n + 1, vector<bool>(sum + 1, false));

        // 2、初始化
        for(int i = 0; i < n; i++)
            dp[i][0] = true;

        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = 1; j < sum + 1; 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]];
            }
        }

        // 4、返回值
        return dp[n][sum / 2];
    }
};

【空间优化 - 滚动数组】 

        原理与第一道经典的01背包问题一样。

C++ 算法代码

class Solution {
public:
    bool canPartition(vector<int>& nums) {

        int n = nums.size();
        int sum = 0;
        for(auto val : nums)
            sum += val;

        if(sum % 2) return false;

        // 1、创建dp表
        vector<bool> dp(sum + 1, false);

        // 2、初始化
        dp[0] = true;

        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = sum + 1; j >= nums[i - 1]; j--)
                dp[j] = dp[j] || dp[j - nums[i - 1]];
        }

        // 4、返回值
        return dp[sum / 2];
    }
};

目标和⭐⭐

494. 目标和 - 力扣(LeetCode)


        用数学知识分析转化成我们常见的「背包模型」的问题。 设我们最终选取的结果中,前面加 + 号的数字之和为 a ,前面加 - 号的数字之和为 b ,整个数组的总和为 sum ,于是我们有:
  • a + b = sum
  • a - b = target
        上面两个式子消去 b 之后,可以得到 a = (sum + target) / 2 也就是说,我们仅需在 nums 数组中选择⼀些数,将它们凑成和为 (sum + target) / 2 即可。

【算法原理】 

 #:状态表示:

        根据「经验 + 题目要求」我们继续尝试「用 i 位置为结尾」结合「题目要求」得到,dp[ i ][ j ] 表示:在前 i 个数中选,总和正好等于 j ,⼀共有多少种选法。

#:状态转移方程:

根据「最后⼀个位置」的元素,结合题目的要求,我们有「选择」最后⼀个元素或者「不选择」最后⼀个元素两种策略:
  • 不选 nums[ i ] :那么我们凑成总和 j 的总方案,就要看在前 i - 1 个元素中选,凑成总和为 j 的方案数。根据状态表示:dp[ i ][ j ] = dp[ i - 1 ][ j ]
  • 选择 nums[ i ] :这种情况下是有前提条件的,此时的 nums[ i ] 应该是小于等于 j 因为如果这个元素都比要凑成的总和大,选择它就没有意义呀。那么我们能够凑成总和为 j 的方案数,就要看在前 i - 1 个元素中选,能否凑成总和为 j - nums[ i ] 。根据状态表示:dp[ i ][ j ] += dp[ i  - 1 ][ j - nums[ i ]]

         综上所述,两种情况如果存在的话,应该要累加在⼀起。因此,状态转移方程为:dp[ i ][ j ] = dp[ i - 1 ][ j ]; if(nums[ i ] <= j)  dp[ i ][ j ] += dp[ i - 1 ][ j - nums[ i ]]

#:初始化:

        由于需要用到「上一行」的数据,因此我们可以先把第一行初始化。 第一行表示不选择任何元素,要凑成目标和 j 。只有当目标和为 0 的时候才能做到,因此第⼀ 行仅需初始化第⼀个元素 dp[0][0] = 1。

#:填表顺序:

        根据「状态转移方程」,我们需要「从上往下」填写每一行,每一行的顺序是「无所谓的」。

#:返回值:

        根据「状态表示」,返回 dp[ n ][ aim ] 的值。其中 n 表示数组的大小, aim 表示要凑的目标和。


C++ 算法代码 

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(auto val : nums)
            sum += val;

        int aim = (sum + target) / 2;
        if(aim < 0 || (sum + target) % 2) return 0;

        int n = nums.size();

        // 1、创建dp表
        vector<vector<int>> dp(n + 1, vector<int>(aim + 1, 0));

        // 2、初始化
        dp[0][0] = 1;
        
        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = 0; j < aim + 1; j++)
            {
                dp[i][j] = dp[i - 1][j];
                if(j >= nums[i - 1])
                    dp[i][j] += dp[i - 1][j - nums[i - 1]];
            }
        }

        // 4、返回值
        return dp[n][aim];
    }
};

【空间优化 - 滚动数组】 

        原理与第一道经典的01背包问题一样。

C++ 算法代码

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(auto val : nums)
            sum += val;

        int aim = (sum + target) / 2;
        if(aim < 0 || (sum + target) % 2) return 0;

        int n = nums.size();

        // 1、创建dp表
        vector<int> dp(aim + 1, 0);

        // 2、初始化
        dp[0] = 1;
        
        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = aim; j >= nums[i - 1]; j--)
                dp[j] += dp[j - nums[i - 1]];
        }

        // 4、返回值
        return dp[aim];
    }
};

最后一块石头的重量Ⅱ⭐⭐⭐ 

1049. 最后一块石头的重量 II - 力扣(LeetCode)


【算法原理】 

先将问题「转化」成我们熟悉的题型。

  • 任意两块石头在⼀起粉碎,重量相同的部分会被丢掉,重量有差异的部分会被留下来。那就相当于在原始的数据的前面,加上「加号」或者「减号」,是最终的结果最小即可。也就是说把原始的石头分成两部分,两部分的和越接近越好。

  • 又因为当所有元素的和固定时,分成的两部分越接近数组「总和的⼀半」,两者的差越小。
        因此问题就变成了:在数组中选择⼀些数,让这些数的和尽量接近 sum / 2 ,如果把数看成物品,每个数的值看成体积和价值,问题就变成了「01 背包问题」。

 #:状态表示:

        根据「经验 + 题目要求」我们继续尝试「用 i 位置为结尾」结合「题目要求」得到,dp[ i ][ j ] 表示在前 i 个元素中选择,总和不超过 j,此时所有元素的「最大和」。

#:状态转移方程:

根据「最后⼀个位置」的元素,结合题目的要求,分情况讨论:
  • 不选 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 ] = max(dp[ i ][ j ], dp[ i - 1 ][ j - stones[ i ]] + stones[ i ]); 

         综上所述,我们要的是最大价值。因此,状态转移方程为:dp[ i ][ j ] = dp[ i - 1 ][ j ]; if(j >= stones[ i ])  dp[ i ][ j ] = max(dp[ i ][ j ], dp[ i - 1 ][ j - stones[ i ]] + stones[ i ]); 

#:初始化:

         由于需要用到上一行的数据,因此我们可以先把第一行初始化。 第一行表示「没有石子」。因此想凑成目标和 j ,最大和都是 0

#:填表顺序:

        根据「状态转移方程」,我们需要「从上往下」填写每一行,每一行的顺序是「无所谓的」。

#:返回值:

  • 根据「状态表示」,先找到最接近 sum / 2 的最大和 dp[n][sum / 2] 。
  • 因为我们要的是两堆石子的差,因此返回 sum - 2 * dp[n][sum / 2]

C++ 算法代码 

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {

        int n = stones.size();
        int sum = 0;
        for(auto val : stones)
            sum += val;
        int half = sum / 2;

        // 1、创建dp表
        vector<vector<int>> dp(n + 1, vector<int>(half + 1, 0));

        // 2、初始化 -- 已经在创建dp表中初始化

        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = 1; j < half + 1; 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][half];
    }
};

【空间优化 - 滚动数组】 

        原理与第一道经典的01背包问题一样。

C++ 算法代码

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {

        int n = stones.size();
        int sum = 0;
        for(auto val : stones)
            sum += val;
        int half = sum / 2;

        // 1、创建dp表
        vector<int> dp(half + 1, 0);

        // 2、初始化 -- 已经在创建dp表中初始化

        // 3、填表
        for(int i = 1; i < n + 1; i++)
        {
            for(int j = half; j >= stones[i - 1]; j--)
                dp[j] = max(dp[j], dp[j - stones[i - 1]] + stones[i - 1]);
        }
        return sum - 2 * dp[half];
    }
};

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

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

相关文章

利用宝塔搭建个人博客:简单而高效的教程

前言 宝塔面板是一款服务器管理软件&#xff0c;支持windows和linux系统&#xff0c;可以通过Web端轻松管理服务器&#xff0c;提升运维效率。 例如&#xff1a;创建管理网站、FTP、数据库&#xff0c;拥有可视化文件管理器&#xff0c;可视化软件管理器&#xff0c;可视化CP…

[C++]异常笔记

我不怕练过一万种腿法的对手,就怕将一种腿法 练一万次的对手。 什么是C的异常 在C中&#xff0c;异常处理通常使用try-catch块来实现。try块用于包含可能会抛出异常的代码&#xff0c;而catch块用于捕获并处理异常。当异常被抛出时&#xff0c;程序会跳过try块中未执行…

Redis6之简介与安装

目录 一、NoSQL NoSQL 特点 使用场景 二、Redis介绍 简介 特性 使用场景 三、Redis安装 1、下载 2、安装 3、启动、停止 4、补充 四、key键操作 一、NoSQL NoSQL 非关系型数据库&#xff1b;存储原理非常简单(典型的数据类型为k-v),不存在繁杂的关系链&#xff…

LabVIEW与Space Wire配合开发

LabVIEW与Space Wire配合开发 Space Wire是欧洲航天局开发的一种高速、点对点、全双工的串行总线网络&#xff0c;以IEEE1355-1995和LVDS 两个商业标准为基础&#xff0c;汲取了1394技术、ATM技术、以太网技术的优点&#xff0c;同时考虑了空间应用的特点&#xff0c;在故障检…

10 - 守护进程深度分析

---- 整理自狄泰软件唐佐林老师课程 查看所有文章链接&#xff1a;&#xff08;更新中&#xff09;Linux系统编程训练营 - 目录 文章目录 1. 会话与终端的关联1.1 思考1.2 新会话关联控制终端的方法1.3 一些相关推论1.4 一些想法1.5 编程实验&#xff1a;会话与终端 2. 守护进程…

RK android13编译环境搭建与常用编译命令

RK android13编译环境搭建与常用编译命令 1 使用清华的源1.1 备份sources.list1.2 用下面的内容替换/etc/apt/sources.list里面的内容1.3 更新源 2 安装编译环境3 常用编译命令3.1 设置项目的编译命令&#xff0c;环境变量3.1 编译所有模块3.2 编译android3.2 编译kernel3.3 编…

linux驱动学习2-pinctrl子系统和gpio子系统

pinctrl子系统 pinctrl 子系统主要用于管理芯片的引脚。 iomuxc节点介绍 首先我们在/ebf-buster-linux/arch/arm/boot/dts/imx6ull.dtsi 文件中查找 iomuxc 节点&#xff0c;可以看到如下定义 iomuxc: iomuxc20e0000 {compatible "fsl,imx6ul-iomuxc";reg <…

【软件环境安装部署】Linux服务器下 Docker 安装 MongoDB 以及 SpringBoot 整合 MongoDB 开发使用

文章目录 安装测试 MongoDB拉取镜像创建和启动容器登录mongo容器&#xff0c;并进入到【admin】数据库创建一个用户&#xff0c;mongo 默认没有用户连接mongo数据库测试数据库&#xff0c;插入一条语句测试数据库&#xff0c;查询刚才插入的语句查看所有数据库开放指定端口navi…

【状态估计】基于线性卡尔曼滤波器和粒子滤波器无人机估计地形高度(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

python基础----11-----闭包、装饰器、单例、工厂、多线程、socket、正则表达式、递归

一 闭包 闭包&#xff1a;通过封装一个函数&#xff0c; 该函数内部又封装一个函数用于返回&#xff0c;使得得到一个无法直接修改值的临时变量&#xff0c;只能通过函数调用修改。闭包的作用&#xff1a;代替全局变量&#xff0c;避免全局变量的滥用。闭包的优缺点&#xff1…

华为荣耀系列uniapp无法USB连接手机调试问题解决方案汇总

华为荣耀系列是一个异常奇葩的手机&#xff0c;经常出现无法调试的问题。 目前我整理出一套完整的切实多次测试可行的解决方案。 一、打开手机的关于手机 设置里面-一直快速点击版本号&#xff0c;连续点10几下。 此时处于开发者模式。 二、打开开发者选项 1、打开开发者…

搭建一个定制版New Bing吧

项目介绍 项目地址&#xff1a;https://github.com/adams549659584/go-proxy-bingai 引用项目简介&#xff1a;用 Vue3 和 Go 搭建的微软 New Bing 演示站点&#xff0c;拥有一致的 UI 体验&#xff0c;支持 ChatGPT 提示词&#xff0c;国内可用&#xff0c;国内可用&#xff…

功能安全开发学习实践心得-概念篇

【写在前面】 记录功能安全开发学习&实践过程中遇到的坎&#xff0c;此篇为概念&#xff08;即行业/标准术语&#xff09;的梳理。实践过程中发现不清楚概念&#xff0c;交流即没法进行&#xff0c;反之&#xff0c;理清概念的过程&#xff0c;即是把整个开发过程串联的过…

50、基于51单片机NRF24l01无线寻物跟踪儿童防丢器系统(程序+原理图+PCB图+设计资料+参考论文+开题报告+元器件清单等)

摘 要 正现代城市生活节奏越来越快,在城市中生活的人们,由于工作、家庭、个人发展、孩子教育、职场竞争等诸多原因,大脑时刻处于紧张状态,容易产生紧张和焦虑情绪,生活压力也越来越大。长期处于这样的状态中,会导致记忆力下降、注意力不集中、容易丢三落四,比如人们常常会记不…

『DevOpse最佳实践』使用Jenkins和Harbor进行持续集成和交付的解决方案

&#x1f4e3;读完这篇文章里你能收获到 全文采用图文形式讲解学会使用Harbor配置项目学会在Jenkins中配置Harbor推送权限使用Jenkins和Harbor进行持续集成的实践感谢点赞收藏&#xff0c;避免下次找不到~ 文章目录 一、准备工作1. 环境准备2. 修改Docker配置文件3. Docker登陆…

Netty实战(十一)

预置的ChannelHandler和编解码器&#xff08;一&#xff09;HTTP和SSL/TLS的添加和使用 一、SSL和TLS添加二、基于Netty的HTTP程序2.1 HTTP解码器、编码器和编解码器2.2 聚合HTTP消息2.3 HTTP压缩 一、SSL和TLS添加 作为一个通讯框架&#xff0c;通讯数据的安全性也是不可或缺的…

LVS+KeepAlived集群

LVSKeepAlived集群 一.KeepAlived的原理 1.1基于什么协议 KeepAlived基于VRRP热备份协议# VRRP协议号112# VRRP组播地址224.0.0.18# VRRP通告报文的TTL值必须是2551.2如何选举Master 1&#xff09;初始化时根据state判断master和backup。 2&#xff09;最终根据优先级决定m…

【小沐学Python】Python实现在线电子书(Sphinx + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建测试工程4、项目文件结构5、编译为本地文件6、编译为http服务7、更改样式主题8、支持markdown9、修改文档显示结构10、项目托管到github11、部署到ReadtheDocs结语 1、简介 Sphinx 是一个 文档生成器 &#xff0c;您也可以把它看成一种工具&…

Win10开启混合现实模拟器

最近要做一个类似工业元宇宙的项目&#xff0c;准备先在Win10上先进行模拟&#xff0c;而Win10已经提供了混合现实模拟器&#xff0c;可以在没有头显的情况下进行模拟。本文讲解如何开启这个模拟器。 微软官方给了一个链接讲述如何开启混合现实模拟器&#xff0c;可以点击这里…

嘀嗒陪诊小程序v1.0.8+小程序前端

嘀嗒陪诊小程序功能相对简单&#xff0c;后台也简捷&#xff0c;如果只是做个陪诊服务的小程序也基本能满足了&#xff0c;整体测试了下海参崴发现BUG&#xff0c;小程序端也能正常为使用&#xff0c;唯一用户授权接口是老的。 应用背景&#xff1a;人口老龄化少子化&#xff…