题目
你总共有 n
枚硬币,并计划将它们按阶梯状排列。对于一个由 k
行组成的阶梯,其第 i
行必须正好有 i
枚硬币。阶梯的最后一行 可能 是不完整的。
给你一个数字 n
,计算并返回可形成 完整阶梯行 的总行数。
示例 1:
输入:n = 5
输出:2
解释:因为第三行不完整,所以返回 2 。
示例 2:
输入:n = 8
输出:3
解释:因为第四行不完整,所以返回 3 。
提示:
-
1 <= n <= 231 - 1
思路
- 可行阶梯为等差数列,那么我们就只用找到小于n的最大等差数列和。
- 上下边界先设置为1和n,那么我们需要考虑的是等差数列和(1+mid)*mid/2是否小于等于n,如果小于等于,那么就增大下界并更新ans,否则减小上界。
- 但是因为整型数据会溢出,所以我们需要进行适当的移项,即(1+mid)*mid/2<=n变成mid<=n/(1+mid)*2(先除以(1+mid)再乘2,否则也会溢出),因为mid至少为1,所以n/(1+mid)*2<=n,如果n能被表示,则右式必然不会溢出。
- 但是又因为C++int类型数据再做除法的时候会截断小数部分,所以我们还需要判断乘2的部分有没有少,因为如果n%(1+mid)是大于0.5的,那么乘2之后就至少要加1,所以需要补上判断n%(1+mid)>=(1+mid)/2?1:0,这样就把前面除法截断但应该补上的1给补全了,剩下都是小数,所以会被截断。
- 所以不妨设result = n/(1+mid)*2+(n%(1+mid)>=(1+mid)/2?1:0),那么如果result>=mid,说明符合要求,那么增加下界继续探索,反之,则result是严格小于mid的(因为小数部分是截断的,不会进位),那么就要缩小上界。
- 不断循环直到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类型呢?这个官解不太行。学我的。