算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II)

这篇具有很好参考价值的文章主要介绍了算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

算法学习——LeetCode力扣动态规划篇3

算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II),LeetCode算法学习,算法,学习,leetcode,c++,c,动态规划

494. 目标和

494. 目标和 - 力扣(LeetCode)

描述

给你一个非负整数数组 nums 和一个整数 target 。

向数组中的每个整数前添加 ‘+’ 或 ‘-’ ,然后串联起所有整数,可以构造一个 表达式 :

例如,nums = [2, 1] ,可以在 2 之前添加 ‘+’ ,在 1 之前添加 ‘-’ ,然后串联起来得到表达式 “+2-1” 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

输入:nums = [1], target = 1
输出:1

提示

1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000

代码解析

这道题根本还是回到了在数组中找到一个集合,使得该集合与其余部分之差为target,通过公式推导:
我们可以知道 该集合的值为:(sum-target)/2;

回溯法

目的是找到和为**(sum-target)/2** 的种类

class Solution {
public:
    int result = 0;
    void backtracking(vector<int>& nums, int target ,int deep ,int sum)
    {
        if(sum > target)return;
        if(sum == target)result++;
        if(deep == nums.size()) return;
        //从任一点开始
        for(int i= deep ; i < nums.size() ;i++)
        {
            backtracking(nums,target , i+1  , sum + nums[i]);
        }
        return;
    }
    int findTargetSumWays(vector<int>& nums, int target) {
       
        int sum = 0 , diff = 0;
        for(auto it:nums) sum += it;
        diff = sum - target;
        if( diff<0 || diff%2==1 ) return 0;
		//回溯找diff/2
        backtracking(nums,diff/2,0 ,0);
        return result;
    }
};

动态规划
  1. 背包定义: dp[i][j] , i是使用0-i的元素,j是背包容量,dp[i][j]是使用这么多个元素恰好凑成j的情况
  2. 初始化:dp[0][0]为1,装满容量为0的背包,有一种方法。dp[0][j],看第一个元素的大小情况,进行赋值1(如果第一个元素为0.则dp[0][0]应该为2),其他层的根据第一层改变.
  3. 遍历顺序:从上往下
  4. 递推公式: dp[i][j]=dp[i-1][j](不需要num[i]就能够凑出j的情况)+dp[i-1][j-nums[i]];(需要num[i]凑出j空间的情况) 最终就能实现,从0-i元素当中组合,得到target的所有情况。
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
       
        int sum = 0 , diff = 0;
        for(auto it:nums) sum += it;
        diff = sum - target;
        if( diff<0 || diff%2==1 ) return 0;
        vector<vector<int>>  dp( nums.size() , vector<int>(diff/2 + 1 , 0) ) ;

        dp[0][0] = 1;
        for(int j=0 ; j<(diff)/2+1 ; j++)
            if(j==nums[0]) dp[0][j] += 1;


        for(int i=1 ; i<nums.size() ;i++)
        {
            for(int j=0 ; j<(diff)/2+1 ; j++)
            {
                if(j>=nums[i])
                    dp[i][j] = dp[i-1][j] + dp[i-1][ j - nums[i]] ;
                else
                    dp[i][j] = dp[i-1][j];
            }
        }      
        return dp[nums.size()-1][(diff)/2];
    }
};

474. 一和零

474. 一和零 - 力扣(LeetCode)

描述

给你一个二进制字符串数组 strs 和两个整数 m 和 n 。

请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。

如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。

示例

示例 1:

输入:strs = [“10”, “0001”, “111001”, “1”, “0”], m = 5, n = 3
输出:4
解释:最多有 5 个 0 和 3 个 1 的最大子集是 {“10”,“0001”,“1”,“0”} ,因此答案是 4 。
其他满足题意但较小的子集包括 {“0001”,“1”} 和 {“10”,“1”,“0”} 。{“111001”} 不满足题意,因为它含 4 个 1 ,大于 n 的值 3 。

示例 2:

输入:strs = [“10”, “0”, “1”], m = 1, n = 1
输出:2
解释:最大的子集是 {“0”, “1”} ,所以答案是 2 。

提示

1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i] 仅由 ‘0’ 和 ‘1’ 组成
1 <= m, n <= 100

代码解析

动态规划(01背包,三级数组)

和经典的背包问题只有一种容量不同,这道题有两种容量,即选取的字符串子集中的 0 和 1 的数量上限。

经典的背包问题可以使用二维动态规划求解,两个维度分别是物品和容量。这道题有两种容量,因此需要使用三维动态规划求解,三个维度分别是字符串、0的容量和 1 的容量。

定义三维数组dp,其中dp[i][j][k] 表示在前 i 个字符串中,使用 j 个 0 和 k 个 1 的情况下最多可以得到的字符串数量。
当 0 和 1 的容量分别是 j 和 k 时,考虑以下两种情况:

  • 如果 j< zeros 或 k<ones,则不能选第 i 个字符串,此时有 dp[i][j][k] = dp[i−1][j][k];

  • 如果 j ≥ zeros 且 k ≥ones,则如果不选第 i个字符串,有dp[i][j][k]=dp[i−1][j][k],如果选第 i个字符串,有 dp[i][j][k]=dp[i−1][j−zeros][k−ones]+1,dp[i][j][k] 的值应取上面两项中的最大值。

因此状态转移方程如下:
算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II),LeetCode算法学习,算法,学习,leetcode,c++,c,动态规划

class Solution {
public:
    int find_0(string s1)
    {
        int num = 0;
        for(auto it:s1) if(it == '0') num++;
        return num;
    }

    int findMaxForm(vector<string>& strs, int m, int n) {

        vector<vector<vector<int>>>  dp( strs.size() ,vector<vector<int>>( m+1 ,vector<int>( n+1,0) ));
        int num_0 = 0,num_1 = 0;
        num_0 = find_0(strs[0]);
        num_1 = strs[0].size() - num_0;
        for(int j=0 ; j<= m ;j++)
        {
            for(int k=0 ; k<= n ;k++)
            {
               
                if( j>= num_0 && k>= num_1)
                    dp[0][j][k] = 1;
            }
           
        }

        
        for(int i=1 ; i<strs.size() ; i++)
        {
            num_0 = find_0(strs[i]);
            num_1 = strs[i].size() - num_0;
            for(int j=0 ; j<=m ;j++)
            { 
                for(int k=0 ; k<=n ;k++)
                {
                    
                     if( j>= num_0 && k>= num_1)
                        dp[i][j][k] = max( dp[i-1][j][k], dp[i-1][j - num_0][k - num_1] + 1);
                     else
                        dp[i][j][k] = dp[i-1][j][k];
                }
            }
        }
        
        int max_num = 0;
        for(int i=0 ; i<strs.size() ; i++)
        {
            if(dp[i][m][n] > max_num) max_num = dp[i][m][n];
            // cout<<dp[i][m][n]<<' ';
        }
            
        return max_num ;
    }
};

动态规划(滑动数组,二级数组)

由于dp[i][][] 的每个元素值的计算只和dp[i−1][][] 的元素值有关,因此可以使用滚动数组的方式,去掉 dp 的第一个维度,将空间复杂度优化到 O(mn)O(mn)。

实现时,内层循环需采用倒序遍历的方式,这种方式保证转移来的是 dp[i−1][][] 中的元素值。

class Solution {
public:
    int find_0(string s1)
    {
        int num = 0;
        for(auto it:s1) if(it == '0') num++;
        return num;
    }

    int findMaxForm(vector<string>& strs, int m, int n) {

        vector<vector<int>>  dp( m+1 ,vector<int>(n+1,0));

        int num_0 = 0,num_1 = 0;
        for(int i=0 ; i<strs.size() ; i++)
        {
            num_0 = find_0(strs[i]);
            num_1 = strs[i].size() - num_0;
            for(int j=m ; j>=num_0;j--)
            { 
                for(int k=n ; k>=num_1 ;k--)
                {
                    dp[j][k] = max( dp[j][k], dp[j - num_0][k - num_1] + 1);
                }
            }
        }
        return dp[m][n] ;
    }
};

518. 零钱兑换 II

518. 零钱兑换 II - 力扣(LeetCode)

描述

给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。

请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。

假设每一种面额的硬币有无限个。

题目数据保证结果符合 32 位带符号整数。

示例

示例 1:

输入:amount = 5, coins = [1, 2, 5]
输出:4
解释:有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

示例 2:

输入:amount = 3, coins = [2]
输出:0
解释:只用面额 2 的硬币不能凑成总金额 3 。

示例 3:

输入:amount = 10, coins = [10]
输出:1

提示

1 <= coins.length <= 300
1 <= coins[i] <= 5000
coins 中的所有值 互不相同
0 <= amount <= 5000

代码解析

完全背包

一看到钱币数量不限,就知道这是一个完全背包。
dp[j]:凑成总金额j的货币组合数为dp[j]

dp[j] (考虑coins[i]的组合总和) 就是所有的dp[j - coins[i]](不考虑coins[i])相加。
所以递推公式:dp[j] += dp[j - coins[i]];

首先dp[0]一定要为1,dp[0] = 1是 递归公式的基础。
从dp[i]的含义上来讲就是,凑成总金额0的货币组合数为1。文章来源地址https://www.toymoban.com/news/detail-851359.html

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=0 ; j<=amount ; j++  )
            {
                if( j>=coins[i] )
                    dp[j] +=  dp[j-coins[i]] ;
                else
                    dp[j] = dp[j];
            }
        }
        return dp[amount];
    }
};

到了这里,关于算法学习——LeetCode力扣动态规划篇3(494. 目标和、474. 一和零、518. 零钱兑换 II)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Leetcode] 416. 分割等和子集、1049. 最后一块石头的重量 II、494. 目标和、474. 一和零

    内容:今天复习下dp数组中的背包问题 分割等和子集 - 能否装满 最后一块石头 - 尽可能装满 目标和 - 有多少种方法装 一和零 - 装满背包有多少个物品 416. 分割等和子集 10背包:用/不用;有容量;有价值 dp[j] : 容量为j,最大价值为dp[j]         重量和价值等价 dp[target] == t

    2024年02月16日
    浏览(27)
  • 算法训练第四十三天|1049. 最后一块石头的重量 II 、494. 目标和、474.一和零

    题目链接:1049. 最后一块石头的重量 II 参考:https://programmercarl.com/1049.%E6%9C%80%E5%90%8E%E4%B8%80%E5%9D%97%E7%9F%B3%E5%A4%B4%E7%9A%84%E9%87%8D%E9%87%8FII.html 题目难度:中等 有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分

    2023年04月09日
    浏览(24)
  • 【Day43】代码随想录之动态规划0-1背包_1049. 最后一块石头的重量 II_494. 目标和_ 474.一和零

    动态规划理论基础 动规五部曲: 确定dp数组 下标及dp[i] 的含义。 递推公式:比如斐波那契数列 dp[i] = dp[i-1] + dp[i-2]。 初始化dp数组。 确定遍历顺序:从前到后or其他。 打印。 出现结果不正确: 打印dp日志和自己想的一样:递推公式、初始化或者遍历顺序出错。 打印dp日志和

    2024年02月22日
    浏览(38)
  • day43 | 1049. 最后一块石头的重量 II、494. 目标和、474.一和零

    目录: 1049. 最后一块石头的重量 II 有一堆石头,用整数数组  stones  表示。其中  stones[i]  表示第  i  块石头的重量。 每一回合,从中选出 任意两块石头 ,然后将它们一起粉碎。假设石头的重量分别为  x  和  y ,且  x = y 。那么粉碎的可能结果如下: 如果  x == y ,那

    2024年02月12日
    浏览(25)
  • 【算法与数据结构】474、LeetCode一和零

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题要找strs数组的最大子集,这个子集最多含有 m m m 个0和 n n n 个1。本题也可以抽象成一个01背包的问题。其中,strs内的元素就是物品,而 m m m 和 n n n 就是背包的维度。 d p [

    2024年01月22日
    浏览(30)
  • 动态规划 Leetcode 494 目标和

    Leetcode 494 学习记录自代码随想录 要点:1.想到±代表其实求的是连个组合的差值,进而记left为正组合,right为负组合,则有 { l e f t − r i g h t = t a r g e t l e f t + r i g h t = s u m left { begin{matrix} left-right=target \\\\ left+right=sum end{matrix} right . { l e f t − r i g h t = t a r g e t l e f t + r

    2024年04月09日
    浏览(44)
  • Leetcode 474 一和零

    题意理解 :         给你一个二进制字符串数组  strs  和两个整数  m  和  n  。         请你找出并返回  strs  的最大子集的长度,该子集中  最多  有  m  个  0  和  n  个  1  。         如果  x  的所有元素也是  y  的元素,集合  x  是集合  y  的 

    2024年01月17日
    浏览(26)
  • 力扣:474. 一和零(动态规划)(01背包)

    给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 示例 1 : 输入: strs = [“10”, “0001”, “111001”, “1”, “0”], m = 5, n = 3 输出:

    2024年01月22日
    浏览(28)
  • leetcode 动态规划(最后一块石头的重量II、目标和、一和零)

    力扣题目链接(opens new window) 题目难度:中等 有一堆石头,每块石头的重量都是正整数。 每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x = y。那么粉碎的可能结果如下: 如果 x == y,那么两块石头都会被完全粉碎; 如果 x != y,那

    2024年02月03日
    浏览(36)
  • 力扣第474题 一和零 c++ 动态规划 01背包

    474. 一和零 中等 相关标签 数组   字符串   动态规划 给你一个二进制字符串数组  strs  和两个整数  m  和  n  。 请你找出并返回  strs  的最大子集的长度,该子集中  最多  有  m  个  0  和  n  个  1  。 如果  x  的所有元素也是  y  的元素,集合  x  是集合  y

    2024年02月06日
    浏览(27)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包