题目要求
背景
山东省小学组(CSP-X)2019第四题。
题目描述
乔治在梦中来到了一个神奇部落,这个部落的神树具有奇特的功能:对于每一位新朋友,都会获赠金币,而且金币的数量会随时间的延续而增加:
第 1 周,每天 1 枚金币;
第 2 周,每天 2 枚金币;
第 3 周,每天 3 枚金币;……
请问:至少多少天,乔治的金币数量达到 n 枚?
输入格式
一行,只有一个正整数 n。
输出格式
一行,一个整数,表示金币达到 n 枚所需的最少天数。
样例输入
30
样例输出
17
提示/说明
【样例说明】
第 1 周:每天 1 枚,共 7 枚;
第 2 周:每天 2 枚,共 14 枚;
第 3 周:每天 3 枚,3 天即可:7+14+3*3=30。
共计:7+7+3 = 17 天。
【数据规模】
对于 30%的数据, n 不超过 2147483647;
对于 100%的数据, n 的位数不超过 18。
解题步骤
这道题属于中等难度,考验代码能力。首先分析一下问题,告诉我们每周可以得多少个金币,求需要多少天。
首先想到的就是for循环,不断用i去试余数,用变量ans累加天数,直到余数为零或不够为止。这很简单,代码如下:
#include<bits/stdc++.h>
#define int long long //捣乱一下很好玩
using namespace std;
main(){
int n,ans=0;
scanf("%lld",&n);
for(int i=1;;i++){
if(n-7*i>0) n-=7*i,ans+=7; //以一个星期7天为单位,可以优化时间复杂度
else if(n-7*i<=0){ //如果不够直接退出
ans+=n/i;
break;
}
}
printf("%lld",ans);
return 0;
}
但是这样的话显而易见:超时!
![](https://img-blog.csdnimg.cn/img_convert/90dd5e9d49d16d979c7afc75e440e77f.png)
来简单计算一下,假设n顶到头,那么n=999,999,999,999,999,999,时间复杂度保守估计差不多是
![](https://img-blog.csdnimg.cn/img_convert/669313ce3798bf42aa60a8c1289ecb26.png)
左右,肯定是超了。那么还得另寻他法。
我们来找找金币的变化规律:
周 | ||||||
金币 |
总数 |
观察一下,金币的数量不就是等差数列吗?
根据等差数列求和公式 和=项数×(项数+1)÷2 ,我们可以知道每个周结束时我们有多少金币。
而且周数是上升的,金币数也是上升的,符合二分查找的单调性,这样就可以使用二分了。二分周数,求出每个周之前的金币数,查找正好比n小的值的下标(找最右值),即周数。如果有多出来的就减去整的,再向上取整,求出多出的天数,再加上原来的天数就行了。
鉴于我解释的比较生硬,我们举一个例子来说一下:
输入:45
那么开始二分下标,得到最右值是3,那么就用
7×3(天数)+(向上取整)ceil ( n- (r×(r+1)÷2)(数列求和公式)÷ r+1 (新的一周的金币数) )
AC代码
到了你们最喜欢的 话不多说上代码!
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; //n用long long
// 二分周数,如果不正好的话算出超出的发了多少天,向上取整
// 等差数列求和公式:项数×(项数+1)÷2
ll kun(ll n){ return (7*n*(n+1))>>1; } //等差数列求和公式(请各位ikun自行忽略函数名)
ll cceil(ll x,ll y){ return x%y==0? x/y:x/y+1; } //自定义向上取整,因为普通ceil会范围超限
int main(){
ll n,ans=0;
scanf("%lld",&n); //最好用scanf,不然可能TLE
ll l=1,r=1e9; //右指针最好设大一点
while(l<=r){ //二分模板
ll mid=(l+r)>>1; //用右移运算,不然可能爆
if(kun(mid)<=n) l=mid+1;
else r=mid-1;
}
printf( "%lld",7*r + cceil( n-kun(r) , r+1 ) ); //个人习惯在括号比较多的时候加上空格,逻辑清晰
return 0;
}
![](https://img-blog.csdnimg.cn/img_convert/ae2ae38ba3275a5a8ef89ad563a0903b.png)
一道有难度的二分题,希望大家能有所收获,掰掰ヾ(•ω•`)o
本文可以转载,请注明作者,谢谢