从零备战蓝桥杯——动态规划(背包dp篇)

news2024/11/15 21:22:40

双非刷leetcode备战2023年蓝桥杯,qwq加油吧,无论结果如何总会有收获!一起加油,我是跟着英雄哥的那个思维导图刷leetcode的,大家也可以看看所有涉及到的题目用leetcode搜索就可以哦,因为避让添加外链,一起加油!!!

动态规划将分为五个板块来讲,本篇为背包问题

请添加图片描述


文章目录

  • 五步走战略
  • 推导公式总结:
  • 遍历顺序总结:
  • 初始化
  • 背包问题:
    • 01背包:
      • 01背包:leetcode相关题目
        • 416. 分割等和子集
        • 1049. 最后一块石头的重量 II
        • 494. 目标和
        • 474. 一和零
    • 完全背包:
    • 好了,很好懂吧,笑死,做做题试试吧。
      • 完全背包:leetcode相关题目
        • 518. 零钱兑换 II
        • 377. 组合总和 Ⅳ
        • 70. 爬楼梯(完全背包dp版)
        • 322. 零钱兑换
        • 279. 完全平方数
        • 139. 单词拆分
    • 多重背包
  • 宝物筛选
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示


五步走战略

    1. 确定dp数组下标含义
    1. 递推公式
    1. 初始化
    1. 遍历顺序
    1. 推导结果

啥也别说了看看自己会走五步了嘛~


推导公式总结:

根据我做题的经验所有的题目的递推公式大概可分为两种;

dp代表最大价值: dp[v]=max(dp[v],dp[v-cost]+weight)
dp代表达到v价值的最大次数: dp[v]=dp[v]+dp[v-cost]
dp代表能不能行: dp[v]=dp[v]||dp[v-cost]


遍历顺序总结:

01背包:物品顺序,容量逆序;

完全背包:物品顺序,容量顺序;
排列先容量再物品,组合先物品再容量;


初始化

最大,初始化为0即可
最小,初始化为能取到的最大值,不然会被覆盖,

然后其他的根据题意取那些特殊的


背包问题:

01背包问题
这是最基本的背包问题,每个物品最多只能放一次。

完全背包问题
第二个基本的背包问题模型,每种物品可以放无限多次。

多重背包问题
每种物品有一个固定的次数上限。


01背包:

有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。
思路:
这是最基本的背包问题,每个物品最多只能放一次
然后如果是二维的相信大家都会写那一维的怎么写呢? f[i][j]=max(f[i-1][j],f[i-1][j-weight[i]]+value[i]);
这里我们只用考虑放和不放就可以了如果用f[v]来表示这个i的价值的话,放就是f[v-cost]+weight不放的话就是f[v],只有这两种情况因为f[v-cost]已经是在v容量下的最大价值了
所以状态转移方程就是 f[v]=max(f[v],f[v-cost]+weight)
那循环咋写呢:外层正序遍历物品,内层逆序遍历容量
这里我们可以发现在二维数组的时候内层正序逆序都可以但是在一维数组的时候只能逆序了。
这是为什么呢???

倒序遍历是为了保证物品i只被放入一次!。但如果一旦正序遍历了,那么物品0就会被重复加入多次!
举一个例子:物品0的重量weight[0] = 1,价值value[0] = 15
如果正序遍历
dp[1] = dp[1 - weight[0]] + value[0] = 15
dp[2] = dp[2 - weight[0]] + value[0] = 30
此时dp[2]就已经是30了,意味着物品0,被放入了两次,所以不能正序遍历。
为什么倒序遍历,就可以保证物品只放入一次呢?
倒序就是先算dp[2]
dp[2] = dp[2 - weight[0]] + value[0] = 15 (dp数组已经都初始化为0)
dp[1] = dp[1 - weight[0]] + value[0] = 15
所以从后往前循环,每次取得状态不会和之前取得状态重合,这样每种物品就只取一次了。
那么问题又来了,为什么二维dp数组历的时候不用倒序呢?
因为对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!

要是不理解可以依靠记忆力,但是十分不建议依靠记忆力,会越用记忆力越不会,千万能理解的别背

好了,很好懂吧,笑死,做做题试试吧。


01背包:leetcode相关题目

416. 分割等和子集

思路:首先,我是真的没看出来这个题用dp做。有点小扯(菜)
这个题我们吧num[i]抽象为物品的重量和价值,而背包的容量为sum/2,转化为01背包问题,在01背包dp后我们便可以得到dp[sum/2]位置上的值,只要这个值和原值一样就return true else return false;

五步走:

    1. 确定dp数组下标含义: dp[i]表示这个数的和,下标i表示的是背包容量
    1. 遍历顺序: 先遍历物品(i),再遍历容量(j)
    1. 然后 递推公式:max(dp[j],dp[j-num[i]]+num[i])
    1. 初始化: 首先:要是数小于两个就分不了了,直接return false;要是和为奇数也分不了了,就出来小数了,直接return false;dp数组全都初始化为0
    1. 推导结果:要是最后我们找到的这个1/2 sum 上的值不等于 1/2sum的话就false否则true

代码:

class Solution
{
public:

    bool canPartition(vector<int> &nums)
    {
        /* 定义dp数组 */
        vector<int> dp(10001, 0);
        /* 数组大小判断 */
        if (nums.size() < 2)
            return false;
        /* 计算目标值即背包容量 */
        int sum = 0;
        int bagWight = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
        }
        /* 和不能为奇数 */
        if (sum % 2 == 1)
            return false;
        /* 计算背包容量 */
        bagWight = sum / 2;
        /* 遍历 */
        for (int i = 0; i < nums.size(); i++)
        { // 遍历物品
            for (int j = bagWight; j >= 0; j--)
            {   // 遍历容量
                /* j是背包容量nums[i]代表物品i所占用的容量 需要判断j >= nums[i]才能放进背包 */
                if (j >= nums[i])
                    dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);
                else
                    continue;
            }
        }
        if (dp[bagWight] == bagWight)
            return true;
        return false;
    }
};

1049. 最后一块石头的重量 II

思路:首先我知道这个是一个01背包问题了然后关键是我知道了,我也不知道怎么转换。。。
哦哦哦哦哦,我会了,我会转换了,太简单了,哈哈哈哈哈你还不会吧,哈哈哈哈哈
你想想哈要是这个大了就返回相减的相等就消掉的话,我们可以理解为什么呢,让这个石头大的那一半放在一边,小的那一半放在另一边,肯定是大的那一半-小的那一半吧,那这个题是不是就是转换为求如何让大的那一堆-小的那一堆出来的值最小。
如何让大的减小的最小呢,我们这两个堆肯定是平均分的,让一个堆-另一个堆最小就让在不超过sum/2的情况下找出最大的来不就的了
那不超过sum/2找最大值不就是我们十分熟悉的01背包嘛
注意:这个题因为有小数的存在所以最后我们要考虑到用总和来减去求出来的dp[sum/2]的最大值的二倍;

五步走:

    1. 确定dp数组下标含义: dp[i]表示这个石头的和,下标i表示的是背包容量
    1. 遍历顺序: 先遍历石头(i),再遍历容量(j)
    1. 然后 递推公式:max(dp[j],dp[j-stones[i]]+stones[i])
    1. 初始化: dp数组全都初始化为0
    1. 推导结果: return totle-2*dp[sum];
      代码部分
class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
    int sum = 0;
    for(int i=0;i<stones.size();i++)
    {
        sum+=stones[i];
    }
    int totle=sum;
    sum=sum/2;
    vector<int>dp(10001,0);
    for(int i=0;i<stones.size();i++)
    {
        for(int j=sum;j>=0;j--)
        {
            if(j>=stones[i])
            {
                dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }
    }
    return totle-2*dp[sum];
    }
};

总结:你要不知道这是个01背包真挺难想的。

494. 目标和

思路:这个题和上一个题是一样的,我们首先可以想到把这个数组可以分为+(suma)的和-(sumb)的他们的总和就是sum而我们要找的target=suma-sumb
sum=suma+sumb
那么我们可以得到suma=(target+sum)/2;
那么就可以求让这个数组=suma的情况有多少种,也就转换到了上一个题的思路了;就是01背包的动态规划直接五步走战略,这里让球的是最大值出现的次数,所以可以分为下五步

五步走:

    1. 确定dp数组下标含义: dp[i]表示到这出现的可能的次数,下标i表示的是背包容量
    1. 遍历顺序: 先遍历数(i),再遍历容量(j)
    1. 然后 递推公式: dp[j]=dp[j]+dp[j-nums[i]];//因为我们要求的是最大出现的次数所以这个地方是不选i的次数和选i次数的最大值的求和
    1. 初始化: dp数组全都初始化为0,在dp[0]上初始化为1只有一种可能性
    1. 推导结果: return dp[suma];

代码:

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
  int sum = 0;
    for(int i=0;i<nums.size();i++)
    {
        sum+=nums[i];
    }//算和
    if(sum+target<0)
    {   
        return 0;
    }
    if((sum+target)%2!=0)
    {
        return 0;
    }
    int suma=(sum+target)/2;//背包量
    
    sum=sum/2;
    
    vector<int>dp(10001,0);
    dp[0]=1;
    for(int i=0;i<nums.size();i++)
    {
        for(int j=suma;j>=0;j--)
        {
            if(j>=nums[i])
            {
                dp[j]=dp[j]+dp[j-nums[i]];
            }
        }
    }
   return dp[suma];
    
    }
};

在这里插入图片描述

474. 一和零

思路:把背包扩成二维,当测试题做吧不给你们思路了先自己写吧,没啥脑筋急转弯都告诉你是01背包了那肯定就简单了自己写吧!!不会了就从头开始看吧!
代码

class Solution
{
public:
    int findMaxForm(vector<string> &strs, int m, int n)
    {
        vector<int> ling(strs.size(), 0);
        vector<int> yi(strs.size(), 0);
        for (int i = 0; i < strs.size(); i++)
        {
            for (int j = 0; j < strs[i].length(); j++)
            {
                if (strs[i][j] == '0')
                {
                    ling[i]++;
                }
                if (strs[i][j] == '1')
                {
                    yi[i]++;
                }
            }
        }
        //初始化;

        int dp[601][601] = {0}; // dp数组代表最大子集个数

        for (int i = 0; i < strs.size(); i++)
        {
            for (int j = m; j >=0; j--)
            {

                for (int k = n; k >=0; k--)
                {
                    if (ling[i] <= j && yi[i] <= k)
                    {
                        dp[j][k] = max(dp[j][k], dp[j - ling[i]][k - yi[i]] + 1);
                    }
                }
            }
        }
        return dp[m][n];
    }
};

骗你玩的小笨蛋五步走思路如下:

五步走:

    1. 确定dp数组下标含义: dp[j][k]]表示到这出现的可能的次数,下标j,k示的是存0和存1的背包容量
    1. 遍历顺序: 先遍历价值(j,k),再遍历容量(i)
    1. 然后 递推公式:dp[j][k] = max(dp[j][k], dp[j - ling[i]][k - yi[i]] + 1);
    1. 初始化: dp数组全都初始化为0
    1. 推导结果: return dp[m][n];

好了01背包到这里~ 你学会了嘛~ 我们来进行下一个专题 完全背包


完全背包:

有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。放入第 i 种物品
的费用是 Ci,价值是 Wi。求解:将哪些物品装入背包,可使这些物品的耗费的费用总
和不超过背包容量,且价值总和最大。

你想咋做?

反正我想的是既然这个可以无限使用那把这个的特点直接加入到01背包的转移公式中不就ok了
F[i, v] = max{F[i − 1, v − kCi] + kWi) 0 ≤ kCi ≤ v}

就是原来的乘以k倍
这跟 01 背包问题一样有 O(V N) 个状态需要求解,但求解每个状态的时间已经不
是常数了,求解状态 F[i, v] 的时间是 O(v/Ci),总的复杂度可以认为是 O(NV ΣV/Ci),是
比较大的。

还能怎么做?

直接把那无限的使用的物品拆开成他的最大件数件然后直接使用ok不

太ok了~但是时间复杂度没有改变

那再想想为什么我们之前01背包的循环里的第二层 对v的循环要使用逆序的方法来做呢?

是为了只选一次

而这次要选多次

直接把01算法的逆序改为顺序就直接解决了这个完全背包的问题!

背包九讲里是这样讲的:
在这里插入图片描述

就是把01背包第二层循环的逆序改为顺序就成功能解决了完全背包的问题

好了,很好懂吧,笑死,做做题试试吧。

完全背包:leetcode相关题目

518. 零钱兑换 II

没啥好讲的:这个是组合问题,就是不考虑各个之间的顺序所以要先遍历物品再遍历容量或者先遍历容量在遍历物品都可以

五步走:

    1. 确定dp数组下标含义: 表示在当前金额下的组合数的个数,下标表示金额容量
    1. 遍历顺序: 先遍历物品(i),再遍历容量(j)(顺序)
    1. 然后 递推公式: dp[j]=dp[j]+dp[j-coins[i]];(和目标和那个一样)
    1. 初始化: 金额为0,那就能选1个,就是不选
    1. 推导结果: return dp[amount];

代码:

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        vector<int>dp(amount+1,0);//表示在当前金额下的组合数的个数,下标表示金额容量
        dp[0]=1;
        for(int i=0;i<coins.size();i++)
        {
            for(int j=coins[i];j<=amount;j++)
            {
                    dp[j]=dp[j]+dp[j-coins[i]];
            }
        }
        return dp[amount];
    }
};

377. 组合总和 Ⅳ

这个是排列问题所以容量循环要在外面,不然就是组合问题了
举个栗子:
如果把遍历nums(物品)放在外循环,遍历target的作为内循环的话,举一个例子:计算dp[4]的时候,结果集只有 {1,3} 这样的集合,不会有{3,1}这样的集合,因为nums遍历放在外层,3只能出现在1后面!
所以就是上个题把循环顺序改一下就好了;
但是这个题还有一个
题目数据保证答案符合 32 位整数范围。
在这里插入图片描述
SomeBody leetcode 不讲武德就非让我WA一次
一定要加上dp[j-nums[i]] < INT_MAX - dp[j]来保证dp[j-nums[i]] + dp[j]< INT_MAX 不超出整数范围

dp[j-nums[i]] + dp[j]< INT_MAX直接这样写没有用因为也会超范围

保证过程不超出整数范围!
代码:

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
   vector<int>dp(target+1,0);//表示在当前金额下的组合数的个数,下标表示金额容量
        dp[0]=1;
       for(int j=0;j<=target;j++)
        {
             for(int i=0;i<nums.size();i++)
            {
                if(nums[i]<=j&& dp[j-nums[i]] < INT_MAX - dp[j])
                    dp[j]=dp[j]+dp[j-nums[i]];
            }
        }
        return dp[target];
    }
};

70. 爬楼梯(完全背包dp版)

思路:排列,完全背包,到n,表示次数,1,2为价格,一共多少台阶就是容量·,懂?
五步走:自己走;
代码:

class Solution {
public:
    int climbStairs(int n) {
        int dp[46]={0};//方法
        dp[0]=1;
        for(int i=0;i<=n;i++)
        {
            for(int j=1;j<=2;j++)
            {
                if(i-j>=0)
                dp[i]=dp[i]+dp[i-j];
            }
        }
    return dp[n];
    }
};

要是这个题改成一次能上好几阶台阶,你有超能力了;那就把第二个循环里的2换成m就ac了

322. 零钱兑换

思路:像这种求的是最小次数而不是最大次数的题啊,那个dp数组啊,就要初始化为能取到的最大值,这里我初始化的值是amont+1;然后和之前一样做就行了;

五步走:

    1. 确定dp数组下标含义: dp[i]表示这个数的最小次数,下标i表示的是背包容量
    1. 遍历顺序: 先遍历物品(i),再遍历容量(j)(这个题都行)
    1. 然后 递推公式: dp[j]=min(dp[j],dp[j-coins[i]]+1);
    1. 初始化: 求最小初始化最大。dp[0]为0
    1. 推导结果:这里我是自己又建立了个求最大相加的值,我看看他和amount相等吗相等就返回不相等说明组不成,直接-1别的方案可以评论区
      代码:
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int>dp(amount+1,);
        vector<int>dp1(amount+1,0); 
              for(int j=1;j<=amount;j++)
        {
                for(int i=0;i<coins.size();i++)
            {
                if(coins[i]<=j)
                {
                    dp[j]=min(dp[j],dp[j-coins[i]]+1);
                    dp1[j]=max(dp1[j],dp1[j-coins[i]]+coins[i]);
                }
            }
        }
        if(dp1[amount]==amount)
        return dp[amount];
        else
        return -1;
    }
};

279. 完全平方数

思路:自己做,我2分钟一把过
提示:和林俊杰有关;
代码:

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+1,n+1);
        dp[0]=0;
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j*j<=n;j++)
            {
                if(j*j<=i)
                {
                    dp[i]=min(dp[i],dp[i-j*j]+1);
                }
            }
        }
        return dp[n];
    }
};

139. 单词拆分

我个人是十分烦这种字符串的题的,我字符串的那种基础比较低我连substr()都不知道是什么鬼东西。。。substr(字符串起点,字符串长度),获取子串;
思路:字符串的完全背包,判断能不能行的问题,选上这个串看看后缀还能不能行,或者不选看看能不能行
五步走:

    1. 确定dp数组下标含义: dp[i]到达i时想不想的等
    1. 遍历顺序: 先遍历物品(i),再遍历容量(j)(这个题都行)(能选多次)
    1. 然后 递推公式: dp[i]=dp[i]||dp[i-wordDict[j].length()];//选或者不选
    1. 初始化:初始化false。dp[0]为true因为没有肯定在这里面
    1. 推导结果:看看到达s.length()位置的时候相不相等;返回return dp[s.length()];
class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        vector<bool> dp(s.length(),false);
        dp[0]=true;
        for(int i=0;i<=s.length();i++)
        {
            for(int j=0;j<wordDict.size();j++)
            {

                if(wordDict[j].length()<=i&&s.substr(i-wordDict[j].length(),wordDict[j].length())==wordDict[j])
                {
                    dp[i]=dp[i]||dp[i-wordDict[j].length()];
                }
            }
        }
        return dp[s.length()];
    }
};

好了,完全背包的题也刷完了,基本上背包的题都刷完了,反正leetcode上是没有多重背包的题了。


多重背包

有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 Mi 件可用,每件耗费的空间是 Ci,价值是 Wi。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。

在这里插入图片描述
我们可以把他拆成01背包或者01背包和完全背包,就是把一个个的分开就行了,当然这种方法十分的复杂


#include<bits/stdc++.h>
 
using namespace std;
const int MAXN=101;
int n,V;
int v[MAXN],w[MAXN],s[MAXN];
int f[MAXN]; 
int main()
{
	cin>>n>>V;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>w[i]>>s[i];
	}
	for(int i=1;i<=n;i++)
	{
		if(s[i]*v[i]>=V)//转化为完全背包 如果大于容量了了就能随便选了就是完全背包
		{
			for(int j=v[i];j<=V;j++)
			{
				f[j]=max(f[j-v[i]]+w[i],f[j]);
			}
		}
		else //转化为 01背包 
		{
			for(int j=V;j>=v[i];j--)
				for(int k=s[i];k>=0;k--)
					if(j>=k*v[i])
						f[j]=max(f[j-k*v[i]]+k*w[i],f[j]);
		}
	}
	cout<<f[V];
 
	return 0;

但是:大神出了新方法了,二进制优化
在这里插入图片描述


#include<bits/stdc++.h>
 
using namespace std;
const int MAXN=1e5;
int n,V;
int v[MAXN],w[MAXN];
int f[MAXN];
int main()
{
	cin>>n>>V;
	int cnt=0;//记录新的物体数 
	for(int i=1,a,b,s;i<=n;i++)
	{
		cin>>a>>b>>s;
		int k=1;
		while(k<=s)//实现1,2,4,8件原物品  合成为新物品 
		{
			v[++cnt]=k*a;
			w[cnt]=k*b;
			s-=k;
			k*=2;
		}
		if(s)
		{
			v[++cnt]=s*a;
			w[cnt]=s*b;
		}
	}
	for(int i=1;i<=cnt;i++)//01背包 
	{
		for(int j=V;j>=v[i];j--)
		{
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	cout<<f[V];
 
	return 0;
}


这两个方法我们接着下面的题讲吧

下面我们来做个题提交去洛谷P1776 宝物筛选提交!!我就不放连接了你们都能找到~
在这里插入图片描述

宝物筛选

题目描述

终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。

这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。

小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 W W W 的采集车,洞穴里总共有 n n n 种宝物,每种宝物的价值为 v i v_i vi,重量为 w i w_i wi,每种宝物有 m i m_i mi 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。

输入格式

第一行为一个整数 n n n W W W,分别表示宝物种数和采集车的最大载重。

接下来 n n n 行每行三个整数 v i , w i , m i v_i,w_i,m_i vi,wi,mi

输出格式

输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。

样例 #1

样例输入 #1

4 20
3 9 3
5 9 1
9 4 2
8 1 3

样例输出 #1

47

提示

对于 30 % 30\% 30% 的数据, n ≤ ∑ m i ≤ 1 0 4 n\leq \sum m_i\leq 10^4 nmi104 0 ≤ W ≤ 1 0 3 0\le W\leq 10^3 0W103

对于 100 % 100\% 100% 的数据, n ≤ ∑ m i ≤ 1 0 5 n\leq \sum m_i \leq 10^5 nmi105 0 ≤ W ≤ 4 × 1 0 4 0\le W\leq 4\times 10^4 0W4×104 1 ≤ n ≤ 100 1\leq n\le 100 1n100

思路:直接多重背包做,纯纯模板题,接着这个题讲讲思路哈;

代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 5000000;
int n, k, v[maxn], w[maxn], m[maxn],cnt,V[maxn],W[maxn],dp[maxn];
int main() {
//初始化
	cin >> n >> k;
	for (int i = 1; i <= n; i++) {
		cin >> v[i] >> w[i] >> m[i];
	}
//拆分的另一种写法
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m[i]; j <<= 1) {
			V[++cnt] = j * v[i];
			W[cnt] = j * w[i];
			m[i] -= j;
		}
		if (m[i] != 0) {
			V[++cnt] = m[i] * v[i];
			W[cnt] = m[i] * w[i];
		}
	}
//01背包
	for (int i = 1; i <= cnt; i++) {
		for (int j =k; j>=W[i]; j--) {
			dp[j] = max(dp[j], dp[j - W[i]] + V[i]);
		}

	}
	cout << dp[k] << endl;
}


好了好了~ 就到这里吧,以后在有题的时候再补充~我们的背包dp问题先到这里吧!

​​在这里插入图片描述

Love is worth years.❤
热爱可抵岁月漫长。

​​​​
本文部分思路来源于网络(做力扣看题解!)如有侵权联系删除~背包部分的内容思路基本来自于大神的背包九讲

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

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

相关文章

【JavaWeb】之Tomcat介绍、安装与使用

【JavaWeb】Tomcat前言一、Tomcat简介1.什么是Tomcat2.其他常用Web服务器二、Tomcat安装1.下载Tomcat2.配置环境变量三、Tomcat使用1.启动与关闭Tomcat2.修改Tomcat的端口号和主机名称3.使用IDEA搭建Web开发环境4.在Tomcat中部署web项目四、Web项目目录结构五、网站访问流程后记…

【Linux】yum vim 基础工具的使用

文章目录1. Linux环境下的软件包管理器---- yum1.2 什么是软件包1.3 yum源镜像配置1.4 yum查看软件包1.5 yum 如何安装软件1.6 yum 如何卸载软件2. Linux编辑器--vim2.1 vim 简介2.2 . vim的基本操作2.3 vim命令模式下的命令集2.3.1 光标移动2.3.2 编辑2.4 vim末行模式下的命令…

Python入门自学进阶-Web框架——24、DjangoAdmin项目应用-定制页面2

这里要实现点击对应的表名称&#xff0c;显示具体表的详细内容&#xff0c;大致的流程是&#xff1a; 前端显示各个表名&#xff0c;如下&#xff1a; <tbody>{% for table_name,admin in app_tables.items %}<tr class"border-bottom"><td style&…

彻底理解Java并发:volatile关键字

本篇内容包括&#xff1a;volatile 关键字简介、volatile 保证可见性&#xff08;包括&#xff1a;关乎不可见性问题描述、JMM内存模型和不可见性的解决方案&#xff09;以及 volatile 其他特性&#xff08;包括&#xff1a;volatile 不保证原子性、volatile 原子性的保证操作、…

mysql-高级命令(1)和一些函数(悟已往之不谏,知来者之可追)

一、高级命令 1.查询某个字段和多个字段 select 某个字段&#xff08;或者多个字段&#xff0c;中间以逗号间隔&#xff09;from 表名&#xff1b; 2. 去除字段的重复值 3.and 表示&#xff08;两个条件都需要满足&#xff09; 4.or &#xff08;两个条件满足一点&#xff09;…

【Linux】软件包管理器 yum 与编辑器 vim 的基本使用

文章目录一、yum 背景知识1、商业生态2、开源生态3、软件生态本土化二、yum 的基本使用1、查看软件包2、安装软件3、卸载软件三、vim 的基本使用1、vim 的基本概念2、vim 的基本操作2.1 模式间切换2.2 光标定位2.3 文本复制2.4 文本编辑2.5 底行模式的操作四、简单 vim 配置2、…

SpringBoot+SpringCloud+Nutty打造分布式在线消息推送服务(实例)

文章目录前言技术架构效果图后端项目消息数据定义存储结构消息状态Nutty消息服务项目结构改动消息bean消息处理器消息转换pojo工具审核消息处理controller实现类服务调用前端连接代码初始化接受消息消息的展示效果总结前言 其实关于这个的话&#xff0c;我先前的几篇博文&…

css选择器

碎碎念&#xff1a;都是一些自己在学习过程中的一点体会&#xff0c;如果有什么不对的感谢大家指正一起学习&#xff01; css选择器一、常用选择器二、属性选择器三、其他五、栗子1. * 通配符2. 空格 div p3. > 子选择器4. ~ 通用兄弟选择器5. 相邻兄弟选择器6. , 选择器分…

使用 Vercel 快速部署前端项目

Vercel&#xff1a;一键部署前端项目。 前端项目部署的问题 先来说下前端项目的部署&#xff0c;一般来说有以下几个步骤&#xff1a; 项目打包上传到服务器域名解析SSL 证书申请Nginx 配置CDN 加速 如果是公司的项目&#xff0c;打包之后的步骤一般有专门的运维人员负责&am…

C++:多态

文章目录一、多态的概念二、多态的定义及实现2.1 多态的构成条件2.2 虚函数2.3 虚函数的重写(覆盖)2.4 override 和 final2.5 重载、覆盖(重写)、隐藏(重定义)的对比三、抽象类四、继承和多态常见的面试问题1.2.总结一、多态的概念 多态按字面的意思就是多种形态。当类之间存在…

瞪羚优化算法(Gazelle Optimization Algorithm,GOA)

瞪羚优化算法&#xff08;Gazelle Optimization Algorithm&#xff0c;GOA&#xff09;由Agushaka等人于2022年提出&#xff0c;该算法模拟了瞪羚逃避捕食者的行为&#xff0c;思路新颖&#xff0c;性能高效。 瞪羚的身高60-110厘米&#xff0c;体重13-29千克。该属物种有像小鹿…

【Linux】基本指令(上)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;操作系统&…

Linux - 第2节 - Linux环境基础开发工具使用

1.Linux 软件包管理器 yum centos 7中安装软件的方式&#xff1a; &#xff08;1&#xff09;源码安装 &#xff08;2&#xff09;rpm包安装 &#xff08;3&#xff09;yum安装 yum安装的好处&#xff1a; &#xff08;1&#xff09;不用编译源码 &#xff08;2&#xff09;不用…

赶紧进来看看---C语言实现学生信息管理系统(2.0动态内存版)

本文介绍了将学生信息管理系统静态版本改造为动态内存版本,主要涉及改造思路,枚举类型的使用,动态内存函数和柔性数组的使用,动手写程序才能使基础知识更为牢固…(文章最后有源码展示) 学生信息管理系统1.0静态版->学生信息管理系统2.0动态内存版 c语言实现学生信息管理系统…

在python中使用ggplot2

python的ggplot2库:plotnine > 一.安装方法&#xff1a; pip install plotnine使用的编译器&#xff1a;pycharm 二.plotnine绘图 1.第一个图形 除了导包的操作不一致&#xff0c;其他类似 from plotnine import ggplot, geom_point, aes, stat_smooth, facet_wrap fr…

70.【JavaScript 6.0】

前端三要素1.前端三要素2.引入JavaScript3.JavaScript基本语法入门4.数据类型5.严格检查模式( use strict)6.Map和Set7.函数的定义和参数获取8.变量的作用域(局部 全局)-------> 和Java一样9.全局规范:10.方法的定义和调用11.内部对象1.Date--------->日期2.JSON--------…

自定义mybatis插件实现sql日志打印

自定义mybatis插件实现sql日志打印 mysql插件实现原理 官网的关键信息 参考文档 https://mybatis.org/mybatis-3/zh/configuration.html#plugins 官方文档 MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下&#xff0c;MyBatis 允许使用插件来拦截的方…

【JavaSE】一篇文章领悟Java运算符

前言&#xff1a; 作者简介&#xff1a;爱吃大白菜1132 人生格言:纸上得来终觉浅&#xff0c;绝知此事要躬行 如果文章知识点有错误的地方不吝赐教&#xff0c;和大家一起学习&#xff0c;一起进步&#xff01; 如果觉得博主文章还不错的话&#xff0c;希望三连支持&#xff01…

Java项目:超市管理系统(java+SSM+JSP+LayUI+jQ+Mysql)

源码获取&#xff1a;俺的博客首页 "资源" 里下载&#xff01; 项目介绍 本项目分为超级管理员、总经理、店长、员工等角色&#xff0c;超级管理员可添加修改删除角色并进行角色菜单配置&#xff1b; 超级管理员角色包含以下功能&#xff1a; 商品管理&#xff1a;添…

C语言高级-4栈

14天阅读挑战赛 目录 一、栈的原理 1、栈的定义 2、栈的应用 &#xff08;1&#xff09;选课问题 &#xff08;2&#xff09;旅游&#xff1a;怎么样把每个城市去且仅去一遍&#xff1f; &#xff08;3&#xff09;栈的使用场景 &#xff08;4&#xff09;思考&#xf…