【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行,是不是比数组模拟简单多了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值