【动态规划算法练习】day17


二维费用的背包问题:

一、474. 一和零

1.题目简介

474. 一和零
给你一个二进制字符串数组 strs 和两个整数 m 和 n 。
请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。
如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。
在这里插入图片描述

2.解题思路

3.代码

class Solution {
public:
    int findMaxForm(vector<string>& strs, int m, int n) {
        vector<int> v(m + 1, 0);//初始化
        vector<vector<int>> dp(n + 1, v);//dp[j][k]表示满足“最多”有j个1和k个0的strs的最大子集的长度(最多:说明并不一定要求放满,因此不用判断dp[j - v1[i]][k - v0[i]]是否存在)
        vector<int> v1(strs.size(), 0);//字符串中1的个数
        vector<int> v0(strs.size(), 0);//字符串中0的个数
        for(int i = 0;i < strs.size(); ++i)//将字符串中的1和0分别存放
        {
            for(int j = 0;j < strs[i].size(); ++j)
            {
                if(strs[i][j] == '1') v1[i]++;
                else v0[i]++;
            }
        }
        for(int i = 0;i < strs.size(); ++i)
        {
            //题意可知这些字符串每个都只能使用一次,属于01背包问题,因此背包的遍历顺序是从后往前
            for(int j = n;j >= v1[i]; --j)
            {
                for(int k = m;k >= v0[i]; --k)
                {
                    dp[j][k] = max(dp[j - v1[i]][k - v0[i]] + 1, dp[j][k]);//放这个字符串和不放
                }
            }
        }
        return dp[n][m];//如果凑不出满足需要的子集,则返回0
    }
};

4.运行结果

在这里插入图片描述

二、879. 盈利计划

1.题目简介

879. 盈利计划
集团里有 n 名员工,他们可以完成各种各样的工作创造利润。
第 i 种工作会产生 profit[i] 的利润,它要求 group[i] 名成员共同参与。如果成员参与了其中一项工作,就不能参与另一项工作。
工作的任何至少产生 minProfit 利润的子集称为 盈利计划 。并且工作的成员总数最多为 n 。
有多少种计划可以选择?因为答案很大,所以返回结果模 10^9 + 7 的值。
在这里插入图片描述

2.解题思路

3.代码

class Solution {
public:
    int profitableSchemes(int n, int minProfit, vector<int>& group, vector<int>& profit) {
        int mod = 1e9 + 7;
        //由题意可知,这是一个01背包问题中的二维费用背包问题(二维指的是人数和利润这两个维度)
        vector<int> v(minProfit + 1, 0);
        vector<vector<int>> dp(n + 1, v);//dp[j][k]表示至少产生k的利润,最多提供j个员工时的计划数
        //初始化利润为0时的dp表
        for(int j = 0;j <= n; ++j)//无论人数是多少,我们都可以找到一个空集的方案
        {
            dp[j][0] = 1;
        }
        for(int i = 0;i < group.size(); ++i)
        {
            for(int j = n;j >= group[i]; --j)//(人数绝对不能是小于0的,因此j要大于等于group[i])
            {
                for(int k = minProfit;k >= 0; --k)//利润可以等于0,但是正常情况下利润是不小于0的,因此k - profit要和0取较大值
                {
                    dp[j][k] += dp[j - group[i]][max(0, k - profit[i])];//求计划数(求组合数,用+)
                    dp[j][k] %= mod;
                }
            }
        }
        return dp[n][minProfit];
    }
};

4.运行结果

在这里插入图片描述

似包非包的问题:

三、377. 组合总和 Ⅳ

1.题目简介

377. 组合总和 Ⅳ
给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
在这里插入图片描述
在这里插入图片描述

2.解题思路

3.代码

class Solution {
public:
//1.多重背包问题:遍历背包要从左往右
//2.本质是求排列个数的问题:对顺序有要求,则先遍历背包再遍历物品
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target + 1, 0);//dp[j]表示凑成总和为j的元素组合个数
        dp[0] = 1;
        for(int j = 0;j <= target; ++j)
        {
            for(int i = 0;i < nums.size(); ++i)
            {
                if(j >= nums[i] && dp[j] < INT_MAX - dp[j - nums[i]])
                dp[j] += dp[j - nums[i]]; //计算组合数、排列数用+
            }
        }
        return dp[target];
    }
};

4.运行结果

在这里插入图片描述

四、96. 不同的二叉搜索树

1.题目简介

96. 不同的二叉搜索树
给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
在这里插入图片描述

2.解题思路

3.代码

class Solution {
public:
    int numTrees(int n) {
        if(n == 0 || n == 1) return 1;
        if(n == 2) return 2;
        vector<int> dp(n + 1, 0);//dp[i]表示当有i个节点时,它所能形成的二叉搜索树的种数
        dp[0] = 1;//空树
        dp[1] = 1;//只有一个根节点
        dp[2] = 2;
        for(int i = 3;i <= n; ++i)
        {
            int left = i - 1, right = 0;//left是左子树的节点个数,right是右子树的节点个数
            while(left >= 0)
            {
                dp[i] += dp[left--] * dp[right++];
            }
        }
        return dp[n];
    }
};

4.运行结果

在这里插入图片描述


总结

今天是算法练习的第17天。
及时当勉励,岁月不待人 ,动态规划算法练习到此告一段落。
来源:力扣(LeetCode),著作权归领扣网络所有。
如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

codeJinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值