题目描述
小 Y 的桌子上放着 n 个苹果从左到右排成一列,编号为从 1 到 n。
小苞是小 Y 的好朋友,每天她都会从中拿走一些苹果。
每天在拿的时候,小苞都是从左侧第 1 个苹果开始、每隔 2 个苹果拿走 1 个苹果。随后小苞会将剩下的苹果按原先的顺序重新排成一列。
小苞想知道,多少天能拿完所有的苹果,而编号为 n 的苹果是在第几天被拿走的?
输入格式
输入的第一行包含一个正整数 n,表示苹果的总数。
输出格式
输出一行包含两个正整数,两个整数之间由一个空格隔开,分别表示小苞拿走所有苹果所需的天数以及拿走编号为 n 的苹果是在第几天。
输入输出样例
输入 #1复制
8
输出 #1复制
5 5
说明/提示
【样例 1 解释】
小苞的桌上一共放了 8 个苹果。
小苞第一天拿走了编号为 1、4、7 的苹果。
小苞第二天拿走了编号为 2、6 的苹果。
小苞第三天拿走了编号为 3 的苹果。
小苞第四天拿走了编号为 5 的苹果。
小苞第五天拿走了编号为 8 的苹果。
【样例 2】
见选手目录下的 apple/apple2.in 与 apple/apple2.ans。
【数据范围】
对于所有测试数据有:1≤n≤109。
测试点 | n≤ | 特殊性质 |
---|---|---|
1∼2 | 10 | 无 |
3∼5 | 103 | 无 |
6∼7 | 106 | 有 |
8∼9 | 106 | 无 |
10 | 109 | 无 |
特殊性质:小苞第一天就取走编号为 n 的苹果。
题目解释
给定编号1-n的苹果排成一列,每天从最左侧开始,每隔2个苹果取走1个(即取走第1,4,7,...个)。需要回答:
第n个苹果在第几天被取走
总共取走了多少个苹果
示例分析
输入n=8时:
-
第1天取走1,4,7号 → 剩余2,3,5,6,8
-
第2天取走2,5,8号 → 8号被取走
-
第3天取走3,6号 输出:8号第2天被取走,共取走7个
解法一:直接模拟法
核心思想
用动态数组实时维护苹果序列,通过循环模拟每天取苹果的过程
复杂度分析
-
时间复杂度:O(nlogn)
-
空间复杂度:O(n)
完整代码
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n;
vector<bool> apples(n, true);
int days = 0, special_day = -1, total = 0;
while (true) {
bool has_apple = false;
vector<int> to_remove;
for (int i = 0; i < n; i += 3) {
if (apples[i]) {
to_remove.push_back(i);
if (i == n - 1 && special_day == -1) {
special_day = days + 1;
}
}
}
if (to_remove.empty()) break;
days++;
total += to_remove.size();
for (int idx : to_remove) {
apples[idx] = false;
}
}
cout << days << " " << special_day;
return 0;
}
解法二:数学规律法
核心发现
-
每天剩余的苹果数构成递推关系:remain = remain - ceil(remain/3)
-
第n个苹果的位置变化遵循:pos = pos - (pos+2)/3
复杂度分析
-
时间复杂度:O(logn)
-
空间复杂度:O(1)
优化实现
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
int days = 0, special_day = 0, remaining = n;
int pos = n - 1; // 转换为0-based索引
while (remaining > 0) {
days++;
int remove_cnt = (remaining + 2) / 3;
if (pos % 3 == 0 && special_day == 0) {
special_day = days;
}
remaining -= remove_cnt;
pos -= (pos + 2) / 3; // 更新位置
}
cout << days << " " << special_day;
return 0;
}
算法对比
维度 |
模拟法 |
数学法 |
---|---|---|
时间复杂度 |
O(nlogn) |
O(logn) |
空间复杂度 |
O(n) |
O(1) |
适用场景 |
n≤1e6 |
n≤1e18 |
优势 |
直观易理解 |
高效精确 |
数学证明
设f(k)为第k天剩余苹果数:
-
递推式:f(k) = f(k-1) - ⌈f(k-1)/3⌉
-
当f(k-1)≡1 mod 3时:f(k)=2f(k-1)/3
-
收敛速度:约log_{1.5}n天
算法总结报告
一、问题本质分析
该问题考察循环取余和数学建模能力,核心在于:
-
周期性取数规律(每3取1)
-
数列递推关系(剩余苹果数呈2/3递减)
-
特殊位置判定(第n个苹果的位置变化)
二、解法对比
维度 |
模拟法 |
数学推导法 |
---|---|---|
时间复杂度 |
O(nlogn) |
O(logn) |
空间复杂度 |
O(n) |
O(1) |
适用数据规模 |
n ≤ 10⁶ |
n ≤ 10¹⁸ |
代码复杂度 |
简单直观 |
需要数学推导 |
调试难度 |
易调试 |
需要验证数学正确性 |
三、算法选择建议
-
竞赛应试:优先掌握数学推导法
-
教学演示:使用模拟法展示过程
-
工程实现:数学法+边界条件处理
四、延伸思考
-
变式1:环形排列时取模运算处理
-
变式2:动态间隔k值的通解公式
-
变式3:多维度取苹果问题(如矩阵排列)