每日leetcode

441. 排列硬币 - 力扣(LeetCode)

题目

你总共有 n 枚硬币,并计划将它们按阶梯状排列。对于一个由 k 行组成的阶梯,其第 i 行必须正好有 i 枚硬币。阶梯的最后一行 可能 是不完整的。

给你一个数字 n ,计算并返回可形成 完整阶梯行 的总行数。

示例 1:

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

示例 2:

输入:n = 8
输出:3
解释:因为第四行不完整,所以返回 3 。

提示:

  • 1 <= n <= 231 - 1

思路

  1. 可行阶梯为等差数列,那么我们就只用找到小于n的最大等差数列和。
  2. 上下边界先设置为1和n,那么我们需要考虑的是等差数列和(1+mid)*mid/2是否小于等于n,如果小于等于,那么就增大下界并更新ans,否则减小上界。
  3. 但是因为整型数据会溢出,所以我们需要进行适当的移项,即(1+mid)*mid/2<=n变成mid<=n/(1+mid)*2(先除以(1+mid)再乘2,否则也会溢出),因为mid至少为1,所以n/(1+mid)*2<=n,如果n能被表示,则右式必然不会溢出。
  4. 但是又因为C++int类型数据再做除法的时候会截断小数部分,所以我们还需要判断乘2的部分有没有少,因为如果n%(1+mid)是大于0.5的,那么乘2之后就至少要加1,所以需要补上判断n%(1+mid)>=(1+mid)/2?1:0,这样就把前面除法截断但应该补上的1给补全了,剩下都是小数,所以会被截断。
  5. 所以不妨设result = n/(1+mid)*2+(n%(1+mid)>=(1+mid)/2?1:0),那么如果result>=mid,说明符合要求,那么增加下界继续探索,反之,则result是严格小于mid的(因为小数部分是截断的,不会进位),那么就要缩小上界。
  6. 不断循环直到left>right,则输出ans即可。

代码实现

class Solution {
public:
    int arrangeCoins(int n) {
        int left = 1, right = n, ans = n;
        while(left <= right) {
            int mid = left + (right-left) / 2;
            int result = n / (1+mid) * 2 + ((n%(1+mid))>=((1+mid)/2)?1:0);
            if(result >= mid) {
                left = mid + 1;
                ans = mid;
            }
            else right = mid - 1;
        }
        return ans;
    }
};

复杂度分析

  • 时间复杂度:O(logn)。
  • 空间复杂度:O(1)。

官方题解

  • 官解用long long代替就很出猫,如果输入数据是long long类型呢?这个官解不太行。学我的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值