LeetCode 1049. 最后一块石头的重量 II

Posted Alex Hub

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 1049. 最后一块石头的重量 II相关的知识,希望对你有一定的参考价值。

1049. 最后一块石头的重量 II

无论按照何种顺序粉碎石头,最后一块石头的重量总是可以表示成
在这里插入图片描述

可以这样理解,将所有的石头分为两堆,ki=1的石头是一堆,ki=-1的石头是另一堆,我们的目标就是求上述和式的最小非负值,即这两堆石头重量之差的绝对值diff也是所有划分当中最小的。

记所有石头的总重量为sum,ki=-1的石头重量之和是neg,则ki=1的石头的重量之和为sum-neg。

则有:
在这里插入图片描述

要使最后一块石头的重量尽可能地小,neg需要在不超过sum/2的前提下尽可能地大。

因此本问题可以看作是背包容量为sum/2,物品重量和价值均为stonesi的 0-1 背包问题。

定义二维布尔数组dp,dp[i+1][j]表示前 i 个石头能否凑出重量 j。

dp[0][] 为不选任何石头的状态,因此除了 dp[0][0] 为真,其余dp[0][j] 全为假。

对于第 i 个石头,考虑凑出重量 j:

  • 若 j<stones[i],则不能选第 i 个石头,此时有 dp[i+1][j]=dp[i][j];
  • 若 j≥stones[i],存在选或不选两种决策,不选时有dp[i+1][j]=dp[i][j],选时需要考虑能否凑出重量 j−stones[i],即 dp[i+1][j]=dp[i][j−stones[i]]。若二者均为假则 dp[i+1][j] 为假,否则dp[i+1][j] 为真。

因此状态转移方程如下:

在这里插入图片描述

求出dp[n][] 后,所有为真的 dp[n][j] 中,最大的 j 即为 neg 能取到的最大值。

代入 sum−2⋅neg 中即得到最后一块石头的最小重量。

由于 dp[i+1][] 的每个元素值的计算只和 dp[i][] 的元素值有关,因此可以使用滚动数组的方式,去掉 dp 的第一个维度。

Code

Python

class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        total = sum(stones)
        n, m = len(stones), total // 2
        dp = [False] * (m + 1)
        dp[0] = True

        for weight in stones:
            for j in range(m, weight - 1, -1):
                dp[j] |= dp[j - weight]

        ans = 0
        for j in range(m, -1, -1):
            if dp[j]:
                ans = total - 2 * j
                break
        return ans

以上是关于LeetCode 1049. 最后一块石头的重量 II的主要内容,如果未能解决你的问题,请参考以下文章

Leetcode刷题Python1049. 最后一块石头的重量 II

LeetCode 1049. 最后一块石头的重量 II

LeetCode 1049 最后一块石头的重量[动态规划] HERODING的LeetCode之路

1049. 最后一块石头的重量 II

1049 最后一块石头的重量 II

1049 最后一块石头的重量 II