01背包面试题系列(二)

01背包面试题系列(二)

题目描述——最后一块石头的重量 II

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:

如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0。

示例 1:

输入:stones = [2,7,4,1,8,1]
输出:1
解释:
组合 2 和 4,得到 2,所以数组转化为 [2,7,1,8,1],
组合 7 和 8,得到 1,所以数组转化为 [2,1,1,1],
组合 2 和 1,得到 1,所以数组转化为 [1,1,1],
组合 1 和 1,得到 0,所以数组转化为 [1],这就是最优值。

示例 2:

输入:stones = [31,26,33,21,40]
输出:5

前言

本文所谈到的题目是01背包问题的一个变种,如果你还不是很了解01背包的话,可以先看这篇文章,该文章主要从0开始介绍了01背包问题,从二维数组到滚动数组再到一维数组,优化过程层层递进,带你从原理到实战完全掌握01背包问题。

问题分析

在读完题目之后,我们的第一感觉就是,如果我们能过恰好将所有的物品分成重量相同的两堆就好了,这样最终剩下的石头的重量就等于0!比如说我们分成的两堆石头的重量分别为[1, 2, 3, 4, 5], [10, 2, 3],那么我们就可以使用[2, 3, 5][10]进行碰撞,这样最后剩下的重量为0, 现在还剩下[1, 4][2, 3],那么我们可以使用[4][3]进行碰撞,那么剩下的为[1, 1][2],那么碰完之后剩下石头的重量就等于0了。

在经过上面的例子之后你看你已经理解为什么我们要将所有的石头尽可能的分成两堆重量一样的了,即使重量不一样我们也需要尽可能的将他们之间的差距变小,最后剩下的物品的重量就等于所有物品的重量和减去重量较小的一堆的2倍。

假设所有的石头的重量和为\(S\),那么我们现在的任务就变成了如果我们有一个篮子,他可以承受的重量为\(\frac{S}{2}\),我们需要从所有的石头当中进行选择,在不超过篮子能够承受的重量的情况下,我们能够在篮子当中放入最大的石头的重量和是多少?如果我们求出的最大的石头的重量和等于\(V\),那么上面一题的答案就等于\(S - 2\times V\)

如果能够恰好装满篮子那么我们恰好能够将所有的石头分成两份重量相等的石头,最终剩下的重量为\(S - 2 \times \frac{S}{2} = 0\)

问题转化

如果你之前已经了解过01背包问题相信在经过上面的分析之后你会发现,这个问题可以转化成一个背包问题,因为我们在经过上面的问题转换之后,我们的任务变成了,对于每个石头我们可以选择一次,它的重量和价值相等,当我们的背包能够承受的重量等于所有石头重量和的一半时,我们能够获取的最大的价值是多少?在上文当中我们说的是,如果我们有一个篮子,他可以承受的重量为\(\frac{S}{2}\),能够放入篮子石头的最大重量,但是我们的设置石头的价值和重量是相等的,因此能够获取的最大价值和承受的最大重量是等价的。

单行数组优化代码

class Solution {
    public int lastStoneWeightII(int[] stones) {
        int sum = 0;
        for (int stone : stones) {
            sum += stone;
        }
        int v = sum / 2 + 1;
        int[] f = new int[v];
        for (int i = 0; i < stones.length; i++) {
            for (int j = v - 1; j >= stones[i]; j--) {
                f[j] = Math.max(f[j], f[j - stones[i]] + stones[i]);
            }
        }
        return sum - 2 * f[v - 1];
    }
}

总结

本文主要给大家介绍最后一块石头的重量 II这个题目,这个题目主要就是一个01背包的变种,主要要的问题就是如何将这个问题转化成01背包问题,这是本文所提到的问题的解决核心,其实在很多算法题当中,最终要的就是需要学会将问题进行转化,将一个不熟悉的问题转化成一个我们熟悉的算法。使用动态规划求解的方法还是比较抽象,可能需要大家花时间好好琢磨一下,希望大家有所收获,我是LeHung,我们下期再见!!!(记得点赞收藏哦!)


更多精彩内容合集可访问项目:https://github.com/Chang-LeHung/CSCore

关注公众号:一无是处的研究僧,了解更多计算机(Java、Python、计算机系统基础、算法与数据结构)知识。

posted @ 2022-07-19 17:21  一无是处的研究僧  阅读(52)  评论(0编辑  收藏  举报