动态规划-背包问题


0-1背包问题指的是n件物品要么选中要么不选,计算选中的物品的价值

完全背包问题则每件物品可以多次选择

[leetcode-416]分割等和子集

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200

输入: [1, 5, 11, 5]
输出: true
解释: 数组可以分割成 [1, 5, 5] 和 [11].
class Solution {
    public boolean canPartition(int[] nums) { 
        int len = nums.length;
        if(len < 2) return false;
        int sum = 0;
        for(int i = 0; i < len; i++){
            sum += nums[i];
        }
        if((sum & 1) == 1) return false;
        int target = sum / 2;
        boolean[][] dp = new boolean[len][target + 1];
        //初始化dp数组
        dp[0][nums[0]] = true;
        //这里是合理的,表示当给定的数正好是target时,满足条件
        for(int i = 0; i < len; i++){
            dp[i][0] = true;
        }
        for(int i = 1; i < len; i++){
            for(int j = 1; j <= target; j++){
                if(j - nums[i] >= 0)
                    dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i]];
            }
        }
        return dp[len - 1][target];
    }
}

[leetcode-494]目标和

给定一个非负整数数组,a1, a2, …, an, 和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

输入:nums: [1, 1, 1, 1, 1], S: 3
输出:5
解释:
-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
一共有5种方法让最终目标和为3。

数组非空,且长度不会超过 20 。
初始的数组的和不会超过 1000 。
保证返回的最终结果能被 32 位整数存下。

class Solution {
    public int findTargetSumWays(int[] nums, int S) {
        int len = nums.length;
        int[][] dp = new int[len][2001];
        //注意使用“+”,这里统计的是个数,达到当前目标值方案的个数
        dp[0][1000 - nums[0]] = 1; dp[0][1000 + nums[0]] += 1;
        for(int i = 1; i < len; i++){
            for(int j = -1000; j < 1001; j++){
            	//能达到当前值的前提是不包括当前元素方案的个数大于0.
                if(dp[i - 1][j + 1000] > 0){
                    dp[i][j + 1000 + nums[i]] += dp[i - 1][j + 1000];
                    dp[i][j + 1000 - nums[i]] += dp[i - 1][j + 1000];
                }
            }
        }
        //注意对S进行判断,因为约束了target范围是[-1000, 1000]
        return S > 1000 ? 0 : dp[len - 1][S + 1000];
    }
}

[leetcode-474]一和零

本题体现了双重背包--字符串为物品,两个背包分别是0和1的个数。本质上仍然是0-1背包问题

在计算机界中,我们总是追求用有限的资源获取最大的收益。
现在,假设你分别支配着 m 个 0 和 n 个 1。另外,还有一个仅包含 0 和 1 字符串的数组。
你的任务是使用给定的 m 个 0 和 n 个 1 ,找到能拼出存在于数组中的字符串的最大数量。每个 0 和 1 至多被使用一次。

注意:
给定 0 和 1 的数量都不会超过 100。
给定字符串数组的长度不会超过 600。

输入: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3
输出: 4
解释: 总共 4 个字符串可以通过 5 个 0 和 3 个 1 拼出,即 "10","0001","1","0" 。
class Solution {
    public int findMaxForm(String[] strs, int m, int n) {
        int len = strs.length;
        int[][][] dp = new int[len + 1][m + 1][n + 1];
        for(int i = 1; i <= len; i++){
            int[] cnt = count(strs[i - 1]);
            for(int j = 0; j <= m; j++){
                for(int k = 0; k <= n; k++){
                    dp[i][j][k] = dp[i - 1][j][k];
                    if(j - cnt[0] >= 0 && k - cnt[1] >= 0)
                        dp[i][j][k] = Math.max(dp[i][j][k], dp[i - 1][j - cnt[0]][k - cnt[1]] + 1);
                }
            }
        }
        return dp[len][m][n];

    }
    public int[] count(String s){
        int len = s.length();
        int[] res = new int[2];
        for(int i = 0; i < len; i++){
            res[s.charAt(i) - '0']++;
        }
        return res;
    }
    
}

[leetcode-377]组合总和 Ⅳ

每个元素可重复选取这点类似完全背包,但不同的是不同的顺序也要记录这一点

给定一个由正整数组成且不存在重复数字的数组,找出和为给定目标正整数的组合的个数。

nums = [1, 2, 3]
target = 4
所有可能的组合为:
(1, 1, 1, 1)
(1, 1, 2)
(1, 2, 1)
(1, 3)
(2, 1, 1)
(2, 2)
(3, 1)
请注意,顺序不同的序列被视作不同的组合。
因此输出为 7。
class Solution {
    public int combinationSum4(int[] nums, int target) {
        int[] dp = new int[target + 1];
        //这里设置成1是合理的,指的是当n==target时符合要求
        dp[0] = 1;
        for(int j = 1; j <= target; j++){
            for(int n : nums){
                if(j - n >= 0)
                    dp[j] += dp[j - n];
            }
        }
        return dp[target];
    }
}

[leetcode-322]零钱兑换

完全背包问题

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

输入: coins = [1, 2, 5], amount = 11
输出: 3 
解释: 11 = 5 + 5 + 1
class Solution {
    public int coinChange(int[] coins, int amount) {
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;
        for(int j = 1; j <= amount; j++){
            for(int coin : coins){
                if(j - coin >= 0)
                    dp[j] = Math.min(dp[j], dp[j - coin] + 1);
            }
        }
        if(dp[amount] == amount + 1){
            dp[amount] = -1;
        }
        return dp[amount];
    }
}

[leetcode-518]零钱兑换II

原文: link.
给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

输入: amount = 5, coins = [1, 2, 5]
输出: 4
解释: 有四种方式可以凑成总金额:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1
class Solution {
    public int change(int amount, int[] coins) {       
        int len = coins.length;       
        //coins数组为空时,根据amount分为两种情况
        if(len == 0){
            if(amount == 0) return 1;
            return 0;
        }
        int[][] dp = new int[len][amount + 1]; 
        for(int i = 0; i <= amount; i += coins[0]){
            dp[0][i] = 1;
        }
        for(int i = 1; i < len; i++){
            for(int j = 0; j <= amount; j++){
                dp[i][j] = dp[i - 1][j];
                if(j - coins[i] >= 0)
                    dp[i][j] += dp[i][j - coins[i]];
            }
        }
        return dp[len - 1][amount];
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值