【2023年csp-j第二轮】第一题解析
我们先看题目
题目描述
小 Y 的桌子上放着 n 个苹果从左到右排成一列,编号为从 11到 n。小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 1 个苹果开始、每隔 2 个苹果拿走 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 n 的苹果是在第几天被拿走的?
输入格式
输入的第一行包含一个正整数 n,表示苹果的总数。输出格式
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 n 的苹果是在第几天。输入输出样例
输入8
输出5 5
说明/提示
【样例 1 解释】小苞的桌上一共放了 8 个苹果。
小苞第一天拿走了编号为 1、4、7 的苹果。
小苞第二天拿走了编号为 2、6 的苹果。
小苞第三天拿走了编号为 3 的苹果。
小苞第四天拿走了编号为 5 的苹果。
小苞第五天拿走了编号为 8 的苹果。【样例 2】
见选手目录下的 apple/apple2.in 与 apple/apple2.ans。
【数据范围】
对于所有测试数据有:1≤n≤10^9。
这道题我们用什么方法做呢?首先不太建议用,也就是大多数人想的用数组模拟的方法,因为这这种方法不是官方给出的,也不被认为是解题最好的思路,这里就不为大家展示代码了。接下来我要说的另外一种方法,用的不是数组,而是-------递归
如果你还不知道递归是什么,可以详细的了解一下我的文章:c++详解递归_c++ 递归_不懂编程的小王的博客-CSDN博客
在此给大家简单的讲一下,递归,就是一个函数反复调用自己的过程,递归被广泛用于累加和,阶乘还有一些特定增长的序列等等程序上。比如斐波那契数列等等。
那有人就问了,那这道题到底用什么思路呢?原来,我们没有必要把第几个苹果在不在全部记下,因为题目并没有让我们输出各个苹果被吃掉的顺序,所以我们每次只需要知道
1.小苞是否拿走第n个苹果
2.苹果是否拿完。
3.小苞本轮拿走了几个苹果
关于第一个条件,如果满足,我们需要输出当时的小苞拿了几次苹果就可以了。那我们如何判断这个条件满不满足呢?就是if(n%3==1),也就是如果剩余的苹果数满足这个条件,就代表小苞会拿走编号为最后一个的苹果。那这个n%3==1是怎么来的呢?这块需要一点数学知识,就是这个拿苹果的周期为:先从4个里面拿两个,之后就每三个里面拿一个,我们发现小苞一次拿的应该是剩余苹果数的第1,4,7,10,13,16……个苹果,而这些数都能满足被3除余1的条件。
第二个条件,我们就判断苹果数!=0,接着算,如果等于0,那我们就可以输出时的小苞拿了几次苹果,然后就可以退出递归了。
第三个条件,也有一个公式(找规律可以得出):拿走的苹果数=(总苹果数-1)/3+1.跟据这个公式我们就可以把总苹果数-由公式得出的小苞本轮要拿走的苹果数就可以了。
接下来,上代码:
#include<bits/stdc++.h>
using namespace std;
int cnt=1;
bool flag=true;
int apple(int n) {
if(n%3==1&&flag==true) { //判断是否拿走编号为n的苹果
cout<<cnt<<" ";
flag=false; //防止再次找到
}
n=n-((n-1)/3+1); //减掉小苞这轮拿走的苹果数
if(n==0) { //判断苹果数是否为0
cout<<cnt; //输出小苞拿苹果的轮数
return 0; //苹果全部拿完,退出递归函数
}
cnt++; //增加小苞拿苹果的轮数
apple(n); //递归,苹果没拿完,接着拿
}
int main() {
int n;
cin>>n;
apple(n); //apple函数自带输出 ,无需添加输出
return 0;
}
只用了23行,是不是比数组模拟简单多了?