1. 问题描述:
下面的图形是著名的杨辉三角形:
如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:1,1, 1, 1, 2, 1, 1, 3, 3,1, 1, 4, 6, 4, 1, ...给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数?
输入格式
输入一个整数 N。
输出格式
输出一个整数代表答案。
数据范围
对于 20% 的评测用例,1 ≤ N ≤ 10;
对于所有评测用例,1 ≤ N ≤ 10 ^ 9。
输入样例:
6
输出样例:
13
来源:https://www.acwing.com/problem/content/3421/
2. 思路分析:
每一个杨辉三角上的数字都对应一个组合数Cr c,其中r和c分别是对应的行数与列数(行数与列数从0开始),因为找的是第一次出现的数字,可以发现左右两部分的数字是对称的,所以只需要看左半部分的数字即可,每一行最大的数字为Cr r/2,最大的数字也是第一次出现的数字,比如第8行出现最大的数字为C7 3,第9行出现的最大的数字为C8 4,我们可以大概估算一下数据规模,因为N最大是10 ^ 9,而C32 16 = 601080390 > 10 ^ 9,所以杨辉三角最多有32行,而且从每一行从左到右,从上到下都是递增的,所以可以使用二分查找,从k = 16行开始寻找,Cr k中r >= 2k,r = max(2k,n),n为当前要寻找的数字,当前最小的数字是C2k k,第一次出现的数字一定满足Cr c中c <= r / 2,例如要找C10 6,其中当k = 6的时候二分查找的数字最小的数字为C12 6一定大于n,只有当k = 4的时候才会最终找到对应的r = 10,C10 6 = C10 4. 这个数字也是第一次出现的数字,二分找到的(r,k)就是n第一次的位置,前面总共有r行,左边有k + 1个数字,所以当前的数字n第一次出现是第r * (r + 1) / 2 + k + 1个数字。
3. 代码如下:
class Solution:
# 求解组合数Ca b, 使用定义来求解
def C(self, a: int, b: int, n: int):
res = 1
i, j = a, 1
while j <= b:
res = res * i // j
# 超过了n说明直接返回False就可以了, 肯定是大于n的
if res > n: return res
i -= 1
j += 1
return res
# 找到当前最小的r判断是否满足条件, r >= 2k
def check(self, k: int, n: int):
l = 2 * k
r = max(n, l)
while l < r:
mid = l + r >> 1
if self.C(mid, k, n) >= n:
r = mid
else:
l = mid + 1
if self.C(r, k, n) != n: return False
# 找到了这个数字
print(r * (r + 1) // 2 + k + 1)
return True
def process(self):
n = int(input())
# 从16开始枚举
for i in range(16, -1, -1):
if self.check(i, n): break
if __name__ == "__main__":
Solution().process()