[CSP-J 2023 T1] 小苹果
注意:真题需要指定文件读写
题目描述
小 Y 的桌子上放着 𝑛n 个苹果从左到右排成一列,编号为从 11 到 𝑛n。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 11 个苹果开始、每隔 22 个苹果拿走 11 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 𝑛n 的苹果是在第几天被拿走的?
输入格式
输入的第一行包含一个正整数 𝑛n,表示苹果的总数。
输出格式
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 𝑛n 的苹果是在第几天。
样例 #1
样例输入 #1
8
Copy
样例输出 #1
5 5
Copy
提示
【样例 11 解释】
小苞的桌上一共放了 88 个苹果。 小苞第一天拿走了编号为 11、44、77 的苹果。 小苞第二天拿走了编号为 22、66 的苹果。 小苞第三天拿走了编号为 33 的苹果。 小苞第四天拿走了编号为 55 的苹果。 小苞第五天拿走了编号为 88 的苹果。
【样例 22】
见选手目录下的 apple/apple2.in 与 apple/apple2.ans。
模拟方法
#include <iostream>
using namespace std;
int main() {
int n;
int day = 0;
int finalDay = 0;
cin >> n;
bool taken[n + 1] = { false };
while (true) {
day++;
bool anyLeft = false;
int count = 0;
for (int i = 1; i <= n; ++i) {
if (!taken[i]) {
anyLeft = true;
if (count % 3 == 0) {
taken[i] = true;
if (i == n) {
finalDay = day;
}
}
count++;
}
}
if (!anyLeft) {
break;
}
}
cout << day << " " << finalDay << endl;
return 0;
}
数学方法
#include <iostream>
#include <cmath>
using namespace std;
int n;
int main() {
freopen("apple.in", "r", stdin);
freopen("apple.out", "w", stdout);
cin >> n;
int cnt = 0, day = -1;
int m = n;
while (m) {
cnt++;
int t = ceil(m / 3.0); // 计算当天拿走的苹果数量,向上取整
if (m % 3 == 1 && day == -1) {
day = cnt; // 记录编号为 n 的苹果被拿走的天数
}
m -= t; // 更新剩余苹果数量
}
cout << cnt << " " << day << endl;
return 0;
}
心得:
题目回顾
- 小苞每天从剩余的苹果中每隔两个拿走一个苹果,直到没有苹果剩下。
- 需要计算:
- 拿走所有苹果所需的天数。
- 编号为
n
的苹果在哪一天被拿走。
数学规律
条件推导
-
拿走的苹果数量:
- 每天拿走的苹果数量
t
是当前剩余苹果数量m
的三分之一(向上取整)。 - ( t = \lceil \frac{m}{3} \rceil )
- 每天拿走的苹果数量
-
在这道题目中,判断编号为
n
的苹果在哪一天被拿走的关键条件是m % 3 == 1
。这个条件的推导和解释如下:问题分析
1.苹果的拿取规则:
- 每天从剩余苹果中,每隔两个拿走一个苹果。
- 也就是从剩余的苹果序列中,每隔三个位置拿走一个苹果。
-
每天拿走的苹果数量:
- 如果剩余的苹果数量为
m
,每天拿走的苹果数量t
为 ( t = \lceil \frac{m}{3} \rceil )。 - 使用向上取整是因为我们要确保即使剩余苹果数量不能整除 3,也要取走至少一个苹果。
- 如果剩余的苹果数量为
-
编号为
n
的苹果:- 题目要求计算编号为
n
的苹果在哪一天被拿走。
- 题目要求计算编号为
-
编号为
n
的苹果在剩余苹果中的位置:- 假设编号为
n
的苹果在剩余苹果m
中的相对位置为p
。
- 假设编号为
-
条件
m % 3 == 1
的解释:- 当
m % 3 == 1
时,剩余苹果的数量m
被 3 除余 1,意味着编号为n
的苹果位于第一组的第一个位置。 - 因为每天拿走的都是每组三个苹果中的第一个,所以编号为
n
的苹果位于第一组的第一个位置时,会在当天被拿走。
- 当
-
判断条件的推导:
- 如果编号为
n
的苹果被拿走的天数还未记录(day == -1
),并且当前剩余苹果数量m % 3 == 1
,那么当天cnt
就是编号为n
的苹果被拿走的天数
- 如果编号为
代码详解
-
输入与输出:
- 使用
freopen
函数读取和输出文件内容(竞赛环境常用)。 - 读取输入的苹果总数
n
。
- 使用
-
变量初始化:
cnt
:记录总共拿走所有苹果所需的天数。day
:记录编号为n
的苹果被拿走的天数。m
:当前剩余的苹果数量,初始化为n
。
-
主循环:
- 循环条件
while (m)
表示当前还有苹果未被拿走。 - 每次循环表示新的一天,计数器
cnt
加 1。
- 循环条件
-
每天拿走的苹果数量计算:
t = ceil(m / 3.0)
计算每天拿走的苹果数量,使用ceil
函数向上取整。
-
判断编号为
n
的苹果被拿走的天数:if (m % 3 == 1 && day == -1)
判断编号为n
的苹果是否在当天被拿走。m % 3 == 1
表示当天编号为n
的苹果位于第一组。day == -1
保证只记录一次编号为n
的苹果被拿走的天数。
-
更新剩余苹果数量:
m -= t
更新当前剩余的苹果数量。
-
输出结果:
- 输出总共拿走所有苹果的天数
cnt
以及编号为n
的苹果被拿走的天数day
。
- 输出总共拿走所有苹果的天数
数学方法的优势
-
时间复杂度:
- 由于每次循环中剩余的苹果数量减少了三分之一,时间复杂度是 ( O(\log n) )。
-
清晰的逻辑:
- 通过数学规律直接计算每天拿走的苹果数量,避免了复杂的模拟操作。
总结
这个数学方法的核心在于发现每天拿走苹果数量的规律,并通过判断 m % 3
来确定编号为 n
的苹果被拿走的天数。通过这段代码的详细解释,希望你对数学规律的应用有更深入的理解。