背包_最值问题(动态规划)

背包问题具备的特征:给定一个targettarget可以是数字也可以是字符串,再给定一个数组numsnums中装的可能是数字,也可能是字符串,问:能否使用nums中的元素做各种排列组合得到target

最大最小问题核心公式:

dp[i] = min(dp[i], dp[i-num]+1) or dp[i] = max(dp[i], dp[i-num]+1)

Tips:

  • 如果是完全背包,即数组中的元素可重复使用,nums放在外循环,target在内循环。且内循环正序。
  • 如果是0-1背包,即数组中的元素不可重复使用,nums放在外循环,target在内循环,且内循环倒序;
  • 如果组合问题(完全背包问题)需"考虑元素之间的顺序,需将target放在外循环,将nums放在内循环,(如T377零钱兑换、T70爬楼梯、T139单词拆分)。

ps:不涉及顺序的完全背包问题,numstarget在内外循环都可以,但建议nums外循环,target内循环。

1.零钱兑换(322-中)

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

你可以认为每种硬币的数量是无限的。

示例

输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

输入:coins = [2], amount = 3
输出:-1

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

思路

法1:动态规划:完全背包问题。可以采用自顶向上的思路。dp[i]达到金额i需要的最少的硬币,那么转移方程为:dp[i] = min(dp[i], dp[i-num]+1) 。
法2:dfs+剪枝:本题通过逆序遍历面值数组,有点类似贪心(先找大面值硬币),因为我们要寻找最小数量的硬币。用for循环进行深度优先搜索,关键:当k + count >= ans 时,即得到的结果大于等于当前最小能达到amount的硬币数量,进行剪枝。但超时。。

代码实现:

class Solution {
    // 动态规划
    public int coinChange(int[] coins, int amount) {
        if (coins == null || coins.length == 0) {
            return 0;
        }
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, amount + 1);
        dp[0] = 0;

        for (int coin : coins) {
            for (int i = coin; i <= amount; i++) {
                // 选取使用or不使用当前硬币的最小值
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
        return dp[amount] > amount ? -1 : dp[amount];
    }
    
    // dfs + 剪枝:超时
    private int ans = Integer.MAX_VALUE;

    public int coinChange(int[] coins, int amount) {
        Arrays.sort(coins);
        dfs(coins, coins.length - 1, 0, amount);
        return ans == Integer.MAX_VALUE ? -1 : ans;
    }

    // cnt:当前已经添加的硬币数量
    private void dfs(int[] coins, int i, int count, int amount) {
        if (amount == 0) {
            ans = Math.min(ans, count);
            return;
        }
        if (i < 0) {
            return;
        }
        // 乘法加速:可以满足当前面值的最大数量,即从最大值开始试
        int a = amount / coins[i];
        for (int k = a; k >= 0 && k + count < ans; k--) {
            dfs(coins, i - 1, count + k, amount - k * coins[i]);
        }
    }
}

2.完全平方数(279-中)

题目描述:给定正整数n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于n。你需要让组成和的完全平方数的个数最少。给你一个整数n ,返回和为 n 的完全平方数的 最少数量

示例

输入:n = 13
输出:2
解释:13 = 4 + 9

输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

思路完全背包问题。类比322零钱兑换思路。定义numsArray记录小于n的完全平方数。

代码1:动态规划

private List<Integer> nums = new ArrayList<>();

public int numSquares(int n) {
    nums = numsArray(n);
    int[] dp = new int[n + 1];
    Arrays.fill(dp, n + 1);
    dp[0] = 0;    // 0不需要完全平方数
    for (int num : nums) {
        for (int i = 1; i <= n; ++i) {
            if (i >= num) {
                dp[i] = Math.min(dp[i], dp[i - num] + 1);
            }
        }
    }
    return dp[n];
}

private List<Integer> numsArray(int n) {
    for (int i = 1; i * i <= n; ++i) {
        nums.add(i * i);
    }
    return nums;
}

代码2:dfs+剪枝

private int ans = Integer.MAX_VALUE;

public int numSquares(int n) {
    // 找到距离n最近(最大)的完全平方数
    int num = (int)Math.sqrt(n);
    int[] nums = new int[num];
    for (int i = 0; i < num; ++i) {
        nums[i] = (i + 1) * (i + 1);
    }
    dfs(nums.length - 1, nums, 0, n);
    return ans == Integer.MAX_VALUE ? -1 : ans;
}

private void dfs(int i, int[] nums, int count, int n) {
    if (n == 0) {
        ans = Math.min(ans, count);
        return;
    }
    if (i < 0) return;
    // 乘法加速
    int a = n / nums[i];
    // 倒序遍历,超过当前最小值ans,剪枝
    for (int k = a; k >= 0 && k + count < ans; --k) {
        dfs(i - 1, nums, k + count, n - k * nums[i]);
    }
}

3.一和零(474-中)

题目描述:给你一个二进制字符串数组strs 和两个整数 mn 。请你找出并返回strs 的最大子集的大小,该子集中 最多 有 m个 0 和 n 个 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 。

思路:本题为01背包问题,只不过有两个背包。dp[i][j]表示达到i个0和j个1的最大子集的大小。注:在进入循环之前,我们要先计算每个字符串中0和1的个数。通过逆序嵌套完成两个背包的目标值求解!

代码1:动态规划

public int findMaxForm(String[] strs, int m, int n) {
    int[][] dp = new int[m + 1][n + 1];
    // 01背包:每个字符串只使用一次
    for (String s : strs) {
        // 计算0和1的数量
        int zeros = 0, ones = 0;
        for (char ch : s.toCharArray()) {
            if (ch == '0') zeros++;
            else ones++;
        }
        for (int i = m; i >= zeros; --i) {
            for (int j = n; j >= ones; --j) {
                // dp[i][j]表示到达i和j的子集数量
                dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1);
            }
        }
    }
    return dp[m][n];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,104评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,816评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,697评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,836评论 1 298
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,851评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,441评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,992评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,899评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,457评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,529评论 3 341
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,664评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,346评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,025评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,511评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,611评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,081评论 3 377
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,675评论 2 359

推荐阅读更多精彩内容