[CSP2023]小苹果-详解

温馨提示

各位可以在评论区多多互动,有问题可以问或讨论,有错敬请指正。

小互动

进入正题

去年10月21日,CCF csp-j组竞赛举办。据各地考生的反响,第一题就不容小觑。

那咱们来会会这道题吧。

题目大赏

[CSP2023]小苹果-详解_第1张图片

(链接自洛谷网)

作为J组竞赛的第一题,他肯定不可能考:

树、图、集合……

好巧不巧,这道题就是一道朴实无华的数学题;

但是又有几个人聪明得想到了这点?包括我还是用模拟写的。

所以那有的人肯定开了数组。

不错,开个数组,int n[1000000086];

我做了个实验。

输入如下代码

#include
using namespace std;
int n[1000000086];
int main()
{
    return 0;
}

 提交一下,发现如下界面

[CSP2023]小苹果-详解_第2张图片意思就是说无法生成可执行程序。

 咱们可以算一下。

一个int字节占空间2Byte,那么开上10的9次方以上的空间,相乘再进行单位换算。那么,最后算得的总占地为1.86G左右。

[CSP2023]小苹果-详解_第3张图片

很明显已经超了。

但细看……

[CSP2023]小苹果-详解_第4张图片

再看, 只有那么一个点是10的9次方,如果你按照相对最大的第9个点,按10的6次方开100086个的话管够。

如果你这样开数组的话,只要你模拟对了,90分就没问题;但是如果你想再争取10分,不好意思,一分也没了[旺柴](虽然我这道题也一分没得)

模拟的思路不多说,但这道题怎么就能用数学思路解呢?

咱们来想

1.拿取时数量是怎么减少的?

从第一个开始,每隔两个拿一个,也就相当于:从第一个开始三人按序分组(多出来的先不管),每组的第一个就拿走;所以有几个整组,起码就拿几个。至于分剩下的那一个或两个,起码比一个多,所以肯定还有一个;如果没有分剩下的,那就不说了。(实际上可以看成是苹果个数的向上舍入,请想一想为什么)

所以若以n表示苹果个数的话,那应该是一个n-=ceil(n*1.0/3)的循环迭代,直到苹果树为零时停止的过程。请注意这个乘1.0,因为它可以使n*1.0成为一个浮点数,而在c++中,整数相除时,商一般会截断(舍弃小数部分)。

所以

#include
using namespace std;
int main()

先把这一串写上;

然后定义n和两个天数计算器(我一般喜欢分开整,不喜欢同时计算),千万记得初始化

然后设计输入n;

这就完了。

int n;
int d1=0,d2=0;
cin>>n;
while(n) n-=ceil(n*1.0/3),d1++;

你已经完成了一半了。

那么,下一步,思考

2.什么样的苹果能够被取到?

“小苞第一天拿走了编号为 1、4、7 的苹果。”

很明显,这些苹果的编号都是3a+1(n为正整数)的。

但是第二天,“小苞第二天拿走了编号为 2、6 的苹果。”

好像没什么规律。但请看下表

原来的苹果标号 第一次取走的苹果 重新给苹果排号 再取苹果
1
2 1
3 2
4
5 3
6 4
7
8 5

很明显,这个3a+1的规律满足每次取完苹果后的重新排号。

所以仍然是这个公式n-=ceil(n*1.0/3),只要当n是"3a+1"这种数,我就可以停止循环,取走最后一个苹果了。

所以上代码

所以再把最后一部分写完

while(n%3!=1) n-=ceil(n*1.0/3),d2++;

但是请注意两个问题:

1.如果这样写,那么当到了该取走最后一个苹果那天,已经检测到当前的苹果数量为“3a+1式,循环直接停止,但日期还未更新,所以要使此处的“d2”从1开始迭代或在输出当中加1

2.第一次循环之所以可以停止,是因为n已经等于0了,若不更新值直接把0带进去,那第二个循环就是恒循环(请各位实验一下) ,所以需要另设一个m存放同一个值,将其作为第二个循环的用品。

所以上代码

#include
using namespace std;
int main()
{
    int n, m, d1 = 0, d2 = 0;
    cin >> n;
    m = n;
    while (n)    n -= ceil(n * 1.0 / 3), d1++;
    while (m % 3 != 1)    m -= ceil(m * 1.0 / 3), d2++;
    cout << d1 << " " << ++d2;
    return 0;
}

篇后问题

这道题的模拟方法,欢迎发在讨论区。

你可能感兴趣的:(精编题解,算法,c++)