温馨提示
各位可以在评论区多多互动,有问题可以问或讨论,有错敬请指正。
小互动
进入正题
去年10月21日,CCF csp-j组竞赛举办。据各地考生的反响,第一题就不容小觑。
那咱们来会会这道题吧。
题目大赏
(链接自洛谷网)
作为J组竞赛的第一题,他肯定不可能考:
树、图、集合……
好巧不巧,这道题就是一道朴实无华的数学题;
但是又有几个人聪明得想到了这点?包括我还是用模拟写的。
所以那有的人肯定开了数组。
不错,开个数组,int n[1000000086];
我做了个实验。
输入如下代码
#include<iostream>
using namespace std;
int n[1000000086];
int main()
{
return 0;
}
提交一下,发现如下界面
意思就是说无法生成可执行程序。
咱们可以算一下。
一个int字节占空间2Byte,那么开上10的9次方以上的空间,相乘再进行单位换算。那么,最后算得的总占地为1.86G左右。
很明显已经超了。
但细看……
再看, 只有那么一个点是10的9次方,如果你按照相对最大的第9个点,按10的6次方开100086个的话管够。
如果你这样开数组的话,只要你模拟对了,90分就没问题;但是如果你想再争取10分,不好意思,一分也没了[旺柴](虽然我这道题也一分没得)
模拟的思路不多说,但这道题怎么就能用数学思路解呢?
咱们来想
1.拿取时数量是怎么减少的?
从第一个开始,每隔两个拿一个,也就相当于:从第一个开始三人按序分组(多出来的先不管),每组的第一个就拿走;所以有几个整组,起码就拿几个。至于分剩下的那一个或两个,起码比一个多,所以肯定还有一个;如果没有分剩下的,那就不说了。(实际上可以看成是苹果个数的向上舍入,请想一想为什么)
所以若以n表示苹果个数的话,那应该是一个n-=ceil(n*1.0/3)的循环迭代,直到苹果树为零时停止的过程。请注意这个乘1.0,因为它可以使n*1.0成为一个浮点数,而在c++中,整数相除时,商一般会截断(舍弃小数部分)。
所以
#include<bits/stdc++.h>
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<bits/stdc++.h>
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;
}
篇后问题
这道题的模拟方法,欢迎发在讨论区。