Day44【动态规划】完全背包、518.零钱兑换 II、377.组合总和 Ⅳ

news2024/12/29 8:59:08

完全背包

文章讲解

视频讲解

有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品都有无限个(也就是可以放入背包多次),求解将物品装入背包里的最大价值

完全背包和01背包问题唯一不同的地方就是,每种物品有无限件

本题代码随想录上只有滚动 dp,不直观,下面我们还是 按照 0-1 背包滚动数组的推导过程,从二维 dp 开始推导。参考资料见此 

还是从动态规划五部曲,从二维 dp 数组推导 

例:

背包最大重量为4。

物品为:

重量价值
物品0115
物品1320
物品2430

每件物品都有无限个!

求解将物品装入背包能得到的最大价值

1、确定 dp 数组下标及值的含义 

dp[i][j]:下标 i, j 表示从下标为 [0 - i] 的物品中任意取放进容量为 j 的背包,dp[i][j] 的值表示从物品下标 [0 - i] 任取物品放进容量为 j 的背包所能装入的最大价值(每个物品可以取无限次)

2、确定递推公式  

dp[i][j] 的值表示从下标为 [0 - i] 的物品中任取,放进容量为 j 的背包所能装入的最大价值,怎么求这个 dp[i][j] 呢?为了求 dp[i][j],肯定需要考虑从下标为 0 到 i 的物品中取物然后装进容量为 j 的背包的装取方案。得到这个 dp[i][j] 的装取方案有两种:一种是不放物品 i 就能得到最大价值,另一种是至少放一件物品 i 才能得到最大价值

  • 不放物品 i:已经确定里面不放物品 i 的最大价值,为物品 0 到 i - 1 装进容量为 j 的背包的最大价值,即 dp[i - 1][j]
  • 至少放一件物品 i:这个需要好好分析。至少放一件进去,则需要给背包预留一件物品 i 重量的位置,即最大价值应为 dp[][j - weight[i]] + value[i],这个表示“至少里面有一件物品 i ”。我们发现还有一个位置的下标没有确定,这个位置的下标含义是选择哪些物品去填充背包预留一个 weight[i] 后剩下的空间。因为是完全背包,就算已经有了一个物品 i,剩余的可用来填充剩余空间的物品范围也应该是 0 到 i,故最大价值为 dp[i][j - weight[i]] + value[i]

两种装取方案中的最大值就是从物品 [0 - i] 任取物品放进容量为 j 的背包所能装入的最大价值,即递推公式:dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])   

注意这个递推公式的前提条件是背包要能装下物品 i,否则只需要考虑不放物品 i 的方案 

3、dp 数组初始化

由递推公式,我们首先观察到 dp[i][j] 的值是由上一行(层)的值及本层靠左的值推出来的。

因此我们可以初始化 dp 数组第一行和第一列的值

初始化第一列,即初始化 dp[i][0],显然,根据 dp 数组下标及值的定义,将物品 0 到 i 装进容量为 0 的背包,啥也装不进去,能装的最大价值为 0

再来看看第一行,即初始化 dp[0][j],显然,根据 dp 数组下标 及值的定义,将物品 0 装进容量为 j 的背包,注意到可以装入多个物品 0,因此,容量 j 能装下 j / weight[0] 个物品 0,其能装下的最大价值就是 j / weight[0] * value[0]

其他位置随意初始化,反正都会被遍历覆盖 

4、确定遍历顺序

可以从上层(上一行)遍历填充到下层(下一行),然后单层中从左遍历填充向右。因为某个位置的值是由上一行(层)的值及本层该位置靠左的值推出来的。只要保证该位置靠上和靠左位置的值是更新后的正确的值

代码中,外层 for 循环遍历物品 i 即可(一个物品的索引代表一行,一行一行遍历,每行从左向右遍历)  

5、打印 dp 数组验证 

代码如下 

void bag_problem_2d() {
    cout << "请输入背包容量:";
    int bagSize;
    cin >> bagSize;
    cout << "请输入物品个数:";
    int n;
    cin >> n;
    cout << "请依次输入物品重量:" << endl;
    vector<int> weight(n);
    for (int i = 0; i < n; ++i) {
        cin >> weight[i];
    }
    cout << "请依次输入物品价值:" << endl;
    vector<int> value(n);
    for (int i = 0; i < n; ++i) {
        cin >> value[i];
    }
    cout << endl; 
    cout << "背包容量:" << bagSize << endl;
    cout << "物品重量:";
    for (auto i : weight)
        cout << i << "\t\t";
    cout << endl;
    cout << "物品价值:";
    for (auto i : value)
        cout << i << "\t\t";
    cout << endl << endl;
    
 
    // 定义dp数组下标及含义:dp[i][j]:从物品0到i中任取,放进容量为bagSize的背包,得到最大价值为dp[i][j]。每个物品可无限次放入 
    vector<vector<int> > dp(n, vector<int>(bagSize + 1, 12345));	// 12345表示其他位置随意初始化都行 
    // 递推公式:dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]]+value[i])
    // 初始化:初始化第一行和第一列 
    for (int j = 0; j <= bagSize; ++j) {
        dp[0][j] = (j / weight[0]) * value[0];	// j / weight[0] 表示容量为j的背包能装下物品0的个数  
    }
    for (int i = 0; i < n; ++i) {
    	dp[i][0] = 0;	// 容量为0的背包无法装下物品,能装下的最大价值为0 
	} 
    // 遍历填充dp数组:一行一行,从左向右填充
    for (int i = 1; i < n; ++i) // 遍历物品(即遍历每一行),第一行已经被初始化了,从第二行开始遍历填充 
        for (int j = 1; j <= bagSize; ++j) // 遍历背包(遍历行中的元素),第一列已经被初始化了,从第二列开始遍历填充 
        {
            if (j >= weight[i])
                dp[i][j] = max(dp[i-1][j], dp[i][j-weight[i]]+value[i]);
            else
                dp[i][j] = dp[i-1][j];
        }
    // 打印dp数组验证
    cout << "dp数组如下:" << endl;
    for (const auto & line : dp) {
        for (const auto item : line)
            cout << item << "\t\t";
        cout << endl;
    }
    return;
}

完全背包:滚动数组

回顾一下二维的递推公式(前提是背包要能装下物品 i)

dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i])

还是之前 0-1 背包的思路,我们能不能只维护一层的数据? 

只维护红框中的一层数据

先看看压缩后的递推公式:因为状态压缩了,dp 数组变成了一维,下标 j 代表背包容量 

dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

居然和 0-1 背包的滚动数组递推公式一样了,但是其实含义是不一样的

左图为原二维 dp 数组,右图是我们压缩后的滚动数组

在二维 dp 数组中,我们求某一个位置(青色)的 dp 值,依赖的是当前位置上一层元素(红色)及当前位置左边元素的值(浅绿色) 

映射到滚动数组中,我们求某一个位置(青色)的 dp 值,依赖的是当前位置元素的旧值(红色)及当前位置左边元素的值(浅绿色)

我们从图中可以看出几个关键点

  • 因为滚动数组维护的是某一层二维 dp 数组的数据,为了实现一层一层遍历填充,外层 for 循环为物品 i,表示在二维数组中需要一层一层填充,每遍历完一轮外层的循环,表明更新完了一层二维 dp 数组
  • 滚动数组的最左边首个元素,对应的是原二维 dp 数组每层的第一列元素,其值是背包容量为 0 时装入物品的最大价值,应该始终为 0
  • 更新每层中的元素时,即更新我们的一维滚动数组时,需要从左向右遍历填充!因为二维数组中我们更新当前层某位置元素依赖的是其正上方元素及其左边的元素(看图),对应到滚动数组中,更新某位置元素依赖的是当前位置的旧值及当前位置左边元素的值(看图)。而浅绿色部分的值是当前层元素,在当前层已被更新了新值,故在滚动数组中,为了保证浅绿色部分(即待更新元素左边元素)是已经在当前层被更新的正确值,需要从左向右遍历

总结一下就是,完全背包的滚动数组代码实现和 0-1 背包的滚动数组代码实现只有两个区别:

  1. 完全背包滚动数组只需要关注最左边第二个元素及其后面的值,因为其首个元素始终为 0 (有人会说,那 0-1 背包第一列不也是啥都装不进去的情况,为啥还需要一直更新?答:因为 0-1 背包有些题目可能会出现重量为 0 的物品,那就能装进去该物品了;而完全背包如果出现重量为 0 的物品,那就能猛猛无限装这个物品,就乱套了,所以完全背包问题不会有重量为 0 的物品,所以其背包容量为 0 的时候,不可能装入物品)
  2. 完全背包滚动数组内层遍历背包容量的顺序是从左向右正序遍历,而 0-1 背包滚动数组内层遍历背包容量的顺序是从右向左倒序遍历(根本原因是:相同的递推公式具有不同的含义。0-1 背包中,倒序遍历的原因是不覆盖滚动数组维护的上一层二维 dp 数组的有用数据;完全背包中,正序遍历的原因是需更新滚动数组维护的当前层二维 dp 数组的有用数据)

代码如下

void bag_problem_1d() {
    cout << "请输入背包容量:";
    int bagSize;
    cin >> bagSize;
    cout << "请输入物品个数:";
    int n;
    cin >> n;
    cout << "请依次输入物品重量:" << endl;
    vector<int> weight(n);
    for (int i = 0; i < n; ++i) {
        cin >> weight[i];
    }
    cout << "请依次输入物品价值:" << endl;
    vector<int> value(n);
    for (int i = 0; i < n; ++i) {
        cin >> value[i];
    }
    cout << "背包容量:" << bagSize << endl;
    cout << "物品重量:";
    for (auto i : weight)
        cout << i << "\t\t";
    cout << endl;
    cout << "物品价值:";
    for (auto i : value)
        cout << i << "\t\t";
    cout << endl << endl;
    
 
    // 定义状态压缩后的dp数组
    vector<int> dp(bagSize + 1);
    // 递推公式:dp[j] = max(dp[j], dp[j-weight[i]]+value[i])
    // 初始化:初始化第一行,i=0那一行,目前dp的值就是第一行 
    for (int j = 0; j <= bagSize; ++j) {
        dp[j] = j / weight[0] * value[0];	// 容量为 j 的背包能装物品0的个数乘物品0的价值 
    }
    // 打印一下第一行的dp数组值
	for (auto item : dp)
		cout << item << "\t\t";
	cout << endl; 
    // 遍历填充dp数组:一行一行填充
    for (int i = 1; i < weight.size(); ++i) // 遍历物品(一层一层遍历,第一行已经被初始化了,从第二行开始) 
    {
    	for (int j = 1; j <= bagSize; ++j) // 遍历背包(正序遍历,且从第二个元素遍历,因为首个元素(二维数组第一列)始终为0) 
        {
            if (j >= weight[i])	// 当能装下物品i才装 
                dp[j] = max(dp[j], dp[j-weight[i]]+value[i]);
//            else	// 装不下物品i 
//                dp[j] = dp[j];
        }
        // 遍历完了一轮外层循环,我们打印看看这层dp数组 
        for (auto item : dp)
        	cout << item << "\t\t";
        cout << endl;
	} 
    return;
}

518.零钱兑换 II 

力扣题目链接/文章讲解

视频讲解

这是一道典型的背包问题,一看到钱币数量不限,就知道这是一个完全背包 

将硬币看成物品,面额看成物品重量,总金额代表背包容量

此时问题就转化为,装满容量为 bagSize 的背包,有几种方法,每个物品可以无限次取

但本题和纯完全背包不一样,纯完全背包是凑成背包最大价值是多少,而本题是要求装满背包的方法数

所以,又得开始推二维 dp,进一步拓展到滚动 dp 了(心累) 

动态规划五部曲,来吧!!!!!!!!

1、确定 dp 数组下标及值的含义 

dp[i][j]:下标 i, j 表示从下标为 [0 - i] 的物品中任意取装满容量为 j 的背包,dp[i][j] 的值表示从物品下标 [0 - i] 任取物品装满容量为 j 的背包有多少种方法

2、确定递推公式 

dp[i][j] 的值表示从下标为 [0 - i] 的物品中任取(每个物品能取多次),装满容量为 j 的背包的方法数,怎么求这个 dp[i][j] 呢?为了求 dp[i][j],肯定需要考虑从下标为 0 到 i 的物品中取物然后装满容量为 j 的背包的装入方案。得到这个 dp[i][j] 的方案有两种:一种是不放物品 i 装满了背包,另一种是至少放了一件物品 i 装满了背包

  • 不放物品 i:已经确定里面不放物品 i 的装入方法数,为物品 0 到 i - 1 装满容量为 j 的背包的方法数,即 dp[i - 1][j]
  • 至少放一件物品 i:至少放一件进去,则首先需要给背包预留一件物品 i 重量的位置,即剩余容量为 j - weight[i],下一步还需要从 0 到 i 选择物品去填充背包的剩余容量(因为是完全背包,物品 i 能被多次选择)。故这种情况下装满容量为 j 的背包的方法数为 dp[i][j - weight[i]]

这两种方案各自方法数加起来就是总的装满容量为 j 的背包的方法数,即递推公式:dp[i][j] = dp[i - 1][j] + dp[i][j - nums[i]] 

注意这个递推公式的前提条件是背包要能装下物品 i,否则只需要考虑不放物品 i 的方案 

3、dp 数组初始化 

由递推公式,我们首先观察到 dp[i][j] 的值是由上一行(层)的值及本层靠左的值推出来的。

因此我们可以初始化 dp 数组第一行和第一列的值

先看第一列:用物品 j 装满容量为 0 的背包的方法数为 1:不装入即可

for (int i = 0; i < weight.size(); ++i) 
{
    dp[i][0] = 1;   // 装满容量为0的背包:1种方法
}

再看第一行:用物品 0 装满容量为 j 的背包的方法数:当容量 j 为物品重量的整数倍,则有一种方案能装满,否则无法装满

for (int j = 1; j <= bagSize; ++j) 
{
    if (j % weight[0] == 1) dp[0][j] = 0;    // 无法装满
    if (j % weight[0] == 0) dp[0][j] = 1;    // 容量为物品0重量整数倍,能装满
}

其余位置随意初始化,反正都会被遍历覆盖 

4、确定遍历顺序

可以从上层(上一行)遍历填充到下层(下一行),然后单层中从左遍历填充向右。因为某个位置的值是由上一行(层)的值及本层该位置靠左的值推出来的。只要保证该位置靠上和靠左位置的值是更新后的正确的值

代码中,外层 for 循环遍历物品 i 即可(一个物品的索引代表一行,一行一行遍历,每行从左向右遍历)

5、打印dp数组验证

代码如下

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // amount为背包容量,coins为物品
        // 定义dp数组下标及值的含义:dp[i][j]下标表示从物品0到i任取放满容量为j的背包,装满背包的方法数为dp[i][j]
        vector<vector<int> > dp(coins.size(), vector<int>(amount + 1));
        // 递推公式:dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]]
        // 初始化dp第一列
        for (int i = 0; i < coins.size(); ++i) {
            dp[i][0] = 1;   // 装满容量为0的背包:1种方法
        }
        // 初始化dp第一行,第一行的第一个已经被初始化为1了
        for (int j = 1; j <= amount; ++j) {
            if (j % coins[0] == 1) dp[0][j] = 0;    // 无法装满
            if (j % coins[0] == 0) dp[0][j] = 1;    // 容量为物品0重量整数倍,能装满
        }
        // 遍历填充:从上往下,从左往右
        for (int i = 1; i < coins.size(); ++i)    // 第一行已经被初始化了,从第二行开始
            for (int j = 1; j <= amount; ++j) {    // 第一列已经被初始化了,从第二列开始
                if (j >= coins[i])   // 容量为j的背包能放下物品i
                    dp[i][j] = dp[i - 1][j] + dp[i][j - coins[i]];
                else    // 放不下物品i,则只考虑不放物品i
                    dp[i][j] = dp[i - 1][j];
            }
        return dp[coins.size() - 1][amount];
    }
};

同样,这道题能够利用滚动 dp 数组优化

递推公式如下

dp[j] = dp[j] + dp[j - coins[i]]

二维 dp 中,当前层某位置的 dp 值由其上一层对应位置的 dp 值当前层该位置左边(已在当前层更新) 的dp 值推出

对应到滚动数组中,某位置的 dp 值由该位置的旧值与该位置左边(已在当前层更新)的 dp 值推出

代码如下

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        // amount为背包容量,coins为物品
        // 定义dp数组下标及值的含义:dp[j]下标表示取物品放满容量为j的背包,装满背包的方法数为dp[j]
        vector<int> dp(amount + 1);
        // 递推公式:dp[j] = dp[j] + dp[j-coins[i]]
        // 初始化原二维dp数组第一行
        for (int j = 0; j <= amount; ++j) {
            if (j % coins[0] == 1) dp[j] = 0;    // 无法装满
            if (j % coins[0] == 0) dp[j] = 1;    // 容量为物品0重量整数倍,能装满
        }
        // 遍历填充:先遍历物品(表示一层一层遍历)
        for (int i = 1; i < coins.size(); ++i)    // 二维dp的第一行已经被初始化了,从第二行开始
            for (int j = 1; j <= amount; ++j) {    // 二维dp的第一列已经被初始化了,从第二列开始。注意正序,保证该层该位置左侧的元素是已在当前层被更新的有效值
                if (j >= coins[i])   // 容量为j的背包能放下物品i
                    dp[j] = dp[j] + dp[j - coins[i]];
                else    // 放不下物品i,则只考虑不放物品i
                    dp[j] = dp[j];
            }
        return dp[amount];
    }
};

377.组合总和 Ⅳ

力扣题目链接/文章讲解

视频讲解

本题要考虑排序!

本题虽然在完全背包章节,但是因为非常规,按照完全背包的思路一步一步走不太好想

直接用动态规划五部曲推导 

1、定义 dp 数组下标及值的含义

dp[j]:j 表示排列中的元素之和等于 j, dp[j] 的值为排列方案数 

2、确定递推公式

考虑排列的最后一个元素。“最后一个”暗藏我们考虑到了排列应有的顺序特性

假设该排列的最后一个元素是 i,对于元素之和等于 j − i 的每一种排列,在最后添加 i 之后即可得到一个元素之和等于 j 的排列,因此在计算 dp[j] 时,应该计算所有的 dp[j - i] 之和,即 dp[j] += dp[j - i]。(前提 j 大于等于 i)

3、dp 数组初始化

dp[0] = 1,表示只有当不选取任何元素时,元素之和才为 0,因此只有 1 种方案 

因为其他位置要用 dp 做累加,故其他位置初始化为0

4、确定遍历顺序

因为 dp[j] 的值依赖于其左边的 dp 值,从左向右遍历,保证待求位置的左边的 dp 值为更新过的正确值

5、打印 dp 数组验证

代码如下

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<unsigned long long> dp(target + 1, 0);
        dp[0] = 1;    // 初始化
        for (int j = 1; j <= target; ++j) {
            for (const auto & i : nums) {    // 计算dp[j]时,需要计算所有dp[j-i]的和
                if (j >= i)
                    dp[j] += dp[j - i];    // 前提是j>=i
            }
        }
        return dp[target];

    }
};

本题没用完全背包的思考过程,反而更简单 


回顾总结 

滚动数组的详细推导思考起来太复杂,可以直接记忆下面的一下小技巧

  • 常规完全背包常规 0-1 背包相比,最大的区别在于:滚动数组时,完全背包内层遍历背包容量的顺序从左向右不用逆序
  • 一般来说,求方案数的递推公式都是 dp[j] += dp[j - w[i]],不过含义有所区别,因此别的代码细节应该做相应修改 

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

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

相关文章

F103ZET6使用FSMC和HAL点亮ILI9341

前言 将标准库下的ILI9341驱动移植到使用CubeMX生成的HAL库环境&#xff0c;并成功运行。 一、STM32CubeMX生成框架 &#xff08;一&#xff09;配置RCC、SYS和时钟树 参见常规配置。 &#xff08;二&#xff09;配置FSMC 1、原理图引脚定义 LCD8080接口使用的引脚主要分…

【数据结构】线性表 ⑤ ( 双循环链表 | 双循环链表特点 | 双循环链表插入操作处理 | 代码示例 - 使用 Java 实现 双循环链表 )

文章目录 一、双循环链表二、双循环链表特点三、双循环链表插入操作处理四、代码示例 - 使用 Java 实现 双循环链表 一、双循环链表 " 双循环链表 " 是 在 单循环链表 的基础上 , 在每个 节点 中 , 新增一个 指针 , 指向 该节点 的 前驱节点 ; 双向循环链表 每个 节…

头歌计算机组成原理实验—运算器设计(8)第8关:乘法流水线设计

第8关&#xff1a;乘法流水线设计 实验目的 学生掌握运算流水线基本概念&#xff0c;理解将复杂运算步骤细分成子过程的思想&#xff0c;能够实现简单的乘法运算流水线。 视频讲解 实验内容 在 Logisim 中打开 alu.circ 文件&#xff0c;在6位补码阵列乘法器中利用5位阵列乘…

React学习笔记三-模块与组件的理解

此文章是本人在学习React的时候&#xff0c;写下的学习笔记&#xff0c;在此纪录和分享。此为第三篇&#xff0c;主要介绍react中的模块与组件。 目录 1.模块与组件 1.1模块 1.2组件 1.3模块化 1.4组件化 2.React面向组件编程 2.1函数式组件 2.2类组件 2.2.1类知识的复…

防火墙(一)

防火墙知识 一、iptables概述二、四表五链四表五链iptables防火墙的使用方法 三、示例操作四、规则的匹配通用匹配&#xff1a;隐含匹配&#xff1a;端口匹配&#xff1a; --sport源端口、--dport目的端口TCP标志位匹配&#xff1a;ICMP类型匹配&#xff1a;显示匹配&#xff1…

Windows 安装MySQL 8.0 超详细教程(mysql 8.0.30)

目录 一、删除以前安装的MySQL服务 1、查找以前是否装有mysql 2、删除mysql &#xff08;1&#xff09;停止mysql服务&#xff1a; &#xff08;2&#xff09;删除mysql服务&#xff1a; 3.检查mysql是否已删除 二、下载mysql二进制包 三、解压二进制包&#xff0c;编辑…

一图看懂 setuptools 模块:一个功能齐全、积极维护且稳定的库,旨在方便打包Python项目,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 setuptools 模块&#xff1a;一个功能齐全、积极维护且稳定的库&#xff0c;旨在方便打包Python项目&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;…

第一个 Rust 程序

目录 必要知识代码示例 Cargo 教程[Rust 输出到命令行](https://www.runoob.com/rust/rust-println.html)资料 必要知识 Rust 语言代码文件后缀名为 .rs 使用 rustc 命令编译 .rs 文件 rustc runoob.rs # 编译 runoob.rs 文件编译后会生成 可执行文件 例如&#xff1a; …

『python爬虫』26. selenium与超级鹰处理复杂验证码的处理(保姆级图文)

目录 1. 图片选择类验证码2. 滑块验证码3. 滑块出错&#xff0c;不加载总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 1. 图片选择类验证码 我们这里查看超级鹰文档 图片验证码返回的是一个 dic 结构为 x1,y1…

【SAM系列】An Alternative to WSSS? An Empirical Study of SAM on WSSS Problems

论文链接&#xff1a;https://arxiv.org/pdf/2305.01586.pdf 论文代码&#xff1a;暂无 目的 WSSS旨在弱标签的情况下&#xff0c;生成高质量的分割伪标签&#xff0c;然后用于全监督的语义分割训练。本文探索用SAM来生成伪标签来替代WSSS方案。 为什么不直接用SAM分割而利用…

《面试1v1》synchronized

源码都背下来了&#xff0c;你给我看这 我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你好&#xff0c;我看到你的简历上写着你熟悉 Java 中的 “synchronized” 关键字。你能给我讲讲它的作…

chatgpt赋能Python-python3反转字符串

Python3反转字符串技巧&#xff1a;让你的代码更高效&#xff01; 你是否曾经在编程时需要对字符串进行反转&#xff0c;但却不知从何入手&#xff1f;Python3提供了简单易用的方法&#xff0c;帮助你更快地反转字符串。本文将介绍Python3中字符串反转的方法以及如何在代码中利…

chatgpt赋能Python-python3如何安装numpy

如何安装numpy&#xff1f; 介绍 在Python编程中&#xff0c;NumPy是使用最广泛的库之一。NumPy是数学和科学计算中的核心模块&#xff0c;主要用于处理数字数据&#xff0c;包括数组计算、线性代数、傅里叶变换、随机数生成等任务。在Python编程中&#xff0c;使用NumPy可以…

chatgpt赋能Python-python3小游戏

Python3小游戏&#xff1a;为你的休闲时光增添乐趣 如果你正处于寻找一款简单好玩的小游戏&#xff0c;那么Python3小游戏将是你的不二之选。作为一名有10年Python编程经验的工程师&#xff0c;我可以说Python3小游戏是一款非常有趣、挑战性适中、易于上手的游戏。接下来&…

工作流 jbpm(图文并茂)

文章目录 1 工作流概述2 jBPM概述3 jBPM开发环境搭建及其配置3.1 准备工作3.2 搭建jBPM开发环境3.3 加入jar包 4 系统数据库表介绍4.1 建表4.2 数据库逻辑关系4.2.1 资源库与运行时的表4.2.2 历史数据表 4.3 表结构4.4 流程操作与数表交互说明 5 相关概念6 流程图说明6.0 快速上…

【学习笔记】Rider调试unity【 联调、断点调试等】(决定弃用vscode了)

目录 一 弃用vscode原委二 Rider调试Unity2.1 启动调试2.2 pausepoint 暂停点2.2.1 使用pausepoint2.2.2 pausepoint&#xfeff;与breakpoint的区别 2.3 不同run configuration区别 三 Rider编辑3.1 补充 四 总结 转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.n…

头歌计算机组成原理实验—运算器设计(4)第4关:16位快速加法器设计

第4关&#xff1a;16位快速加法器设计 实验目的 帮助学生理解成组进位产生函数&#xff0c;成组进位传递函数的概念&#xff0c;熟悉 Logisim 平台子电路的概念&#xff0c;能利用前述实验封装好的4位先行进位子电路以及4位快速加法器子电路构建16位、32位、64位快速加法器&a…

Android 12.0 原生SystemUI下拉通知栏UI背景设置为圆角背景的定制(一)

1.前言 在12.0的系统rom定制化开发中,在原生系统SystemUI下拉状态栏的通知栏的背景是白色四角的背景,由于在产品设计中,需要把四角背景默认改成圆角背景,所以就需要分析系统原生下拉通知栏的每条通知的默认背景,然后通过systemui的通知流程,设置默认下拉状态栏UI中的通知…

java+nodejs+vue+python+php家教信息管理系统

任何网友都可以自由地查看、搜索、发布该家教信息平台的信息。该平台是区别于传统的家教中介的服务平台。学生可以免费查看网站上的家教信息&#xff0c;挑选适合自己的家教&#xff1b;教师可以免费查看网站上的需求信息&#xff0c;挑选适合自己的学生&#xff1b;学生可以发…

chatgpt赋能Python-python3安装scrapy

Python3安装Scrapy&#xff1a;提高爬虫效率的关键 如果你正在从事数据科学、数据分析工作&#xff0c;或者需要收集并存储某些特定网站的数据&#xff0c;Scrapy是一个值得尝试的Python3框架。Scrapy是一个开源的Web爬虫框架&#xff0c;可以方便、高效、快速地对数据进行收集…