在问 · 算法题解

441. 排列硬币

题目描述

手上有 n 枚硬币,要把它们摆成阶梯:第 1 行放 1 枚,第 2 行放 2 枚,第 i 行放 i 枚,依次往下。

硬币用完时,最后一行可能没有放满。

返回放满了的行一共有几行。

示例 1
输入:n = 5
输出:2

解释:前两行用掉 3 枚,剩下 2 枚不够第 3 行的 3 枚,答案为 2。

示例 2
输入:n = 8
输出:3

解释:前三行用掉 6 枚,剩下 2 枚不够第 4 行,答案为 3。

数据范围

  • 1 <= n <= 2^31 - 1

解题思路

摆满 k 行需要 k(k+1)/2 枚,它随 k 递增。求最大的 k 使 k(k+1)/2 <= n,在 [0, n] 上二分。是“找最后一个满足”,所以满足时 lo = mid,不满足时 hi = mid - 1,mid 要向上取整。

为什么正确

lo 始终满足 lo(lo+1)/2 <= n;大于 hi 的 k 都不满足。

复杂度分析

时间 O(log n),空间 O(1)。

易错点

mid 必须向上取整 (lo + hi + 1) // 2,否则 lo = mid 时可能原地不动、死循环。mid(mid+1) 会超 32 位,要用 64 位整数。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

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

载入编辑器,自己跑一遍 →

其他解法

二分查找只用整数运算,稳妥且通用;求根公式是 O(1),但要注意浮点精度。

解法 2:数学(求根公式) 时间 O(1),空间 O(1)

前 k 行需要 k(k+1)/2 枚硬币。解不等式 k(k+1)/2 ≤ n,得 k = ⌊(√(8n+1) − 1) / 2⌋。用浮点开方得到近似值后,再做一两次整数校正,消除精度误差。

一次计算直接得出结果。依赖浮点开方,n 很大时必须做整数校正,否则可能差 1。

class Solution:
    def arrangeCoins(self, n: int) -> int:
        # 解 k(k+1)/2 <= n,得 k = floor((sqrt(8n+1)-1)/2)
        k = int((math.sqrt(8 * n + 1) - 1) / 2)
        while (k + 1) * (k + 2) // 2 <= n:
            k += 1
        while k * (k + 1) // 2 > n:
            k -= 1
        return k

二分查找 专题的其他题目

查看 二分查找 全部题目 →