题干分析:
本题介绍了一种游戏,当给定一个数n的时候,利用在第i排放i个数,然后等到排到第n个数时,若第j排排满了j个数,则返回j,如果没有排满则返回j-1。
解题分析:
这个游戏有点类似于给你一袋硬币,然后在第i排上放i个硬币,等你把所有的硬币按照这个规则排完之后看哪排排满了对应的硬币数。为了方便理解我们将所给的数n想象成给了n个硬币。
由于我们有了n个硬币,那么我们是不是要进行排,那么有两种思考的方式,一种是排一个就在n个硬币的技术上减一个,知道把n个硬币全部排完,然后找出排满硬币的行数,另外一种思考方式为进行排列然后边排边记我们已经排了多少个硬币,当记的数和n相等时就停止排列并计数。这两种思考方式都是可以解决本题的,本文介绍利用第二种思路来进行解题,第一种思路读者可以尝试自己解本题,希望能够和小编进行交流。
其实仔细观察的话如果要进行计数的话其实这是一个等差数列并且公差为1的求和问题,对于这种数列求前i项和的公式为(1+i)*i/2,我们可以利用该公式结合n大致的求出该数可能把第i行排满,然后在进行i+1的相加,如果所加结果大于n了就返回i,如果没有就再进行后续的相加,直到大于n为止,然后返回最后所相加的行数-1。
问题解决:
public class Solution {
public int arrangeCoins(int n) {
if(n<0)
return -1;
if(n<1073741823){
int i=(int)Math.floor(Math.sqrt(n*2)-1);
int sum=(1+i)*i/2;
int flag=i;
while(sum<=n){
flag=i;
i++;
sum+=i;
}
return flag;
}
else{
int i=(int)Math.floor(Math.sqrt(n)*1.414213-1);
int sum=0;
if(i%2==0){
sum=i/2;
sum*=(i+1);
}else{
sum=(1+i)/2;
sum*=i;
}
int flag=i;
while(sum<=n){
flag=i;
i++;
sum+=i;
if(sum<0)
return flag;
}
return flag;
}
}
}
本题总结:
1. 在解该题的时候由于没有考虑到所给的数达到了int型变量的最大值的情况,所以在利用等差数列前n项和公式求行数i的时候的时候在乘2的时候会超过范围的情况,所以进行进行了分情况处理。又i*i/2<i*(i+1)/2<(i+1)*(i+1)/2,所以当所给的数大于最大的int/2时,可以先进行对该数开根号,然后在乘根号2处理,由于计算根号2可能会影响效率,这里用1.414213近似代替。根据得到的行数计算现在已经进行的统计计数总和,然后就可以用上述所提的方法来进行解本题了。
2. 在进行叠加的时候如果计数总和大于int的范围的话会返回一个负数,从而让程序陷入一个死循环中,可以在当计数变成<0的时候就认为其已经超过范围了,然后跳出循环。
3. 保留小于最大的int/2部分的代码的目的是为了对于所给数小于最大的int除2的时候代码效率更高。
4. 最终由于LeetCode上该题是一个带锁的,所以提交以后由于解出该题人数的局限,所以没办法看自己打败了多少对手。
读者在读到这篇文章有任何问题或者有什么想了解的欢迎进行留言相互交流,共同进步!^_^