题解-金币(小学组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;
}

但是这样的话显而易见:超时!

来简单计算一下,假设n顶到头,那么n=999,999,999,999,999,999,时间复杂度保守估计差不多是

左右,肯定是超了。那么还得另寻他法。

我们来找找金币的变化规律:

金币

总数

 

 

 

观察一下,金币的数量不就是等差数列吗?

根据等差数列求和公式 和=项数×(项数+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;
}

一道有点点难度的二分题,希望大家能有所收获,掰掰ヾ(•ω•`)o

本文可以转载,请注明作者,谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值