题目描述
难度分:1500
输入n(1≤n≤1018),表示有n颗石子。
A和B取石子,每次A先取走k颗石子(石子剩余个数不足k则全取),然后B取走⌊rest10⌋颗石子(其中rest表示剩余石子数,⌊.⌋表示对.向下取整)。如此交替取石子,直到石子全部取完为止。
输出最小的k,使得A总共可以取走至少一半的石子,即⌈n2⌉颗石子,其中⌈.⌉表示对.向上取整。
输入样例
68
输出样例
3
算法
二分答案
可以比较容易发现单调性,k越大A是越有可能获得超过一半的石子的。
分析一下这个游戏过程,A每次都取k,规律很明显。而B第一次取⌊n−k10⌋,第二次取⌊n−k−⌊n−k10⌋10⌋,很容易就发现B的收益成指数级别下降。模拟这个游戏过程只需要log10n不到的轮次就能因为B的收益为0而停止,把剩下石子直接加到A的所得上即可,这样一来check函数就可以在O(log10n)的时间复杂度下完成任务。
外面就在[1,n]的范围内进行二分答案,时间复杂度是O(log2n)。对于给定的k,能够使得A的所得不少于总数的一半就记录答案往左搜索看有没有更小的k,否则就往右搜索。
复杂度分析
时间复杂度
根据算法的游戏过程分析,二分的时间复杂度为O(log2n),check的时间复杂度为O(log10n)。算法整体的时间复杂度为O(log2nlog10n),大概就是O((logn)2)级别。
空间复杂度
仅使用了有限几个变量,因此算法的额外空间复杂度为O(1)。
python代码
n = int(input())
def check(k: int) -> bool:
a, b, rest = 0, 0, n
while rest >= k:
a += k
rest -= k
gain = rest // 10
b += gain
rest -= gain
if gain == 0:
break
a += rest
return a*2 >= n
l, r = 1, n
while l < r:
mid = l + r >> 1
if check(mid):
r = mid
else:
l = mid + 1
print(r)