leedcode441 排列硬币

排列硬币

你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行可能是不完整的。
给你一个数字 n ,计算并返回可形成完整阶梯行的总行数。
注意: 1 < = n < = 2 31 − 1 1 <= n <= 2^{31}-1 1<=n<=2311

示例: 在这里插入图片描述

输入:n = 5
输出:2
解释:因为第三行不完整,所以返回 2 。

因为排列硬币时,最后一行是可以不完整的,所以我们在有限的硬币数量内尽可能地占有更多行。

假设有 x x x 行,那么 x x x 行一共有 x ( x + 1 ) 2 \frac {x(x+1)} {2} 2x(x+1) 个硬币,所以占有的硬币数要尽可能接近提供的硬币数 n n n 且不能大于 n n n

解法一:二分查找

因为 x x x 的取值范围较大,所以我们可以用二分查找来提高效率。二分查找的思想也很简单,所以直接上代码:

代码如下:

class Solution:
    def arrangeCoins(self, n: int) -> int:
        left, right = 1, n
        while left < right:
            mid = (left + right + 1) // 2
            if mid * (mid + 1) <= 2 * n:
                left = mid
            else:
                right = mid - 1
        return left

这里有个小细节需要注意,就是在求mid时语句为:
mid = (left + right +1) // 2

这里+1的原因:普通二分在查找时,若不能整除,则取左边或者右边都可以,但是在这个排列硬币的题目中,我们需要让值尽量靠右些,因为我们要找硬币数最接近 n n n 的结果。考虑如下情况,比如left=1,right=2,中间点为1.5,普通二分中取1或者2皆可,但往往是取1,这道题是要逼近右边界,考虑循环退出条件,如果取1的话,就永远无法退出循环了。

解法二:数学公式

因为我们知道 x x x 层硬币的总数为 x ( x + 1 ) 2 \frac {x(x+1)} {2} 2x(x+1) ,那么我们不妨直接令:
x ( x + 1 ) 2 = n \frac {x(x+1)} {2}=n 2x(x+1)=n

对上述方程进行求解,显然最终求得的结果就为 ⌊ x ⌋ \lfloor{x}\rfloor x

解得: x 1 = − 1 + 8 n + 1 2 x 2 = − 1 − 8 n + 1 2 x_{1}=\frac {-1+\sqrt{8n+1}} {2} x_{2}=\frac {-1-\sqrt{8n+1}} {2} x1=21+8n+1 x2=218n+1

显然舍去 x 2 x_2 x2 ,那么结果就为 ⌊ x 1 ⌋ \lfloor{x_1}\rfloor x1

代码如下:

class Solution:
    def arrangeCoins(self, n: int) -> int:
        return int((pow(8 * n + 1, 0.5) - 1) / 2)

解法三:位运算

一开始看到这个题解还以为是什么新奇的做法,其实本质上还是循环枚举每一种可能性,但是因为 1 < = n < = 2 31 − 1 1 <= n <= 2^{31}-1 1<=n<=2311,所以使用位运算可以增加效率。

首先,我们用ans来表示最终得结果,初始时ans = 0

因为我们要不断循环,所以我们需要额外一个变量来对ans进行修改。因为答案最长也不会超过16位( I n t . M A X = 16 \sqrt{Int.MAX}=16 Int.MAX =16

不妨令这个变量为mask,初始时,mask = 100000000000000,每次循环时将1进行右移。

在每轮循环时,首先将ansmask按位或,如果按位或之后ans*(ans+1)/2 > n,代表当前结果已经超过了所给硬币数,那么就将ansmask按位异或,即将ans恢复到本轮循环没开始得状态,继续下一轮循环。

代码如下:

class Solution(object):
    def arrangeCoins(self, n):
        """
        :type n: int
        :rtype: int
        """
        ans = 0
        mask = 1 << 15
        while mask > 0:
            ans |= mask
            if ans*(ans+1)/2>n:
                ans ^= mask
            mask >>= 1 
        return ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值