1049. 最后一块石头的重量 II Python 位运算 16 ms , 13 MB


执行用时:16 ms, 在所有 Python 提交中击败了100.00%的用户
内存消耗:13 MB, 在所有 Python 提交中击败了74.16%的用户

题目

有一堆石头,每块石头的重量都是正整数。

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

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

示例:

输入:[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],这就是最优值。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/last-stone-weight-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

题目分析

两个石头,两两相减,最后剩下一个最小值。有点消消乐的意思。。。
1.石头的重量都是整数
2.石头可以完全消除
3.可以两个石头消除一个石头。即1-n,n-n,n-1进行消除

在不理解题目本质的情况下只能用搜索的模式取解决

为了更好的理解和解决问题,我们把问题抽象成数学问题
有N个石头A,B,C,D,E。。。
A可和B消除,也可以不消除
我们假设最优情况是
1.A - (B-C) - (D-E)…
2.A - B - C - D - E…
3.B - A - (C - D - E)…

通过上面的假设,很容易发现
其实题目的数学模型非常简单
就是ABCDE石头前面可以正负两种情况,求最后的和最小
即把石头分成两堆,尽量分的更均匀,两堆的差值最小

解题思路

那有没有办法在遍历一次石头的情况下 解决这个问题呢?
我们反过来思考 ,如果要在遍历一次石头的情况下,我们需要什么
1.我们要能记录下来石头两种情况的累加状态(正,负状态)
2.找到最接近 石头总和/2 的记录

首先解决第一个问题,我们找出小于等于 石头总和/2 的那堆石头
我们遍历石头堆
第一个石头A 出现两状态(加|不加) 记为A(2)
第二个石头B 出现两状态(加|不加) 记为B(4) = A(2) * 2
第三个石头C 出现两状态(加|不加) 记为C(8) = B(2) * 2

第n个石头N 出现两状态(加|不加) 记为N(2^n)

我们可以用一个字典,把2^n种情况的不同结果全部记录下来
然后找到最接近 石头总和/2 的结果
这样就做到了在遍历一次石头堆的情况下,找到最佳结果

优化:用字典保存每次的状态比较复杂,可以使用位运算来完成 更简单方便

record = (record | record << i)
就能记录每次状态的叠加结果

代码

class Solution(object):
    def lastStoneWeightII(self, stones):
        """
        :type stones: List[int]
        :rtype: int
        """
        if len(stones) == 1:
            return stones[0]
        sum_all = sum(stones)
        half = sum_all // 2
        record = 1

        for i in stones:
            record = (record | record << i)

        record = bin(record)[-half-1:]
        less_stones = len(record) - record.find('1') - 1
        return sum_all - less_stones - less_stones

作者:liye-5u
链接:https://leetcode-cn.com/problems/last-stone-weight-ii/solution/1049-zui-hou-yi-kuai-shi-tou-de-zhong-li-2irn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值
>