按规律拿东西

[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;
}

心得:

题目回顾

  • 小苞每天从剩余的苹果中每隔两个拿走一个苹果,直到没有苹果剩下。
  • 需要计算:
    1. 拿走所有苹果所需的天数。
    2. 编号为 n 的苹果在哪一天被拿走。

数学规律

条件推导

  1. 拿走的苹果数量:

    • 每天拿走的苹果数量 t 是当前剩余苹果数量 m 的三分之一(向上取整)。
    • ( t = \lceil \frac{m}{3} \rceil )
  2. 在这道题目中,判断编号为 n 的苹果在哪一天被拿走的关键条件是 m % 3 == 1。这个条件的推导和解释如下:

    问题分析

    1.苹果的拿取规则:

    • 每天从剩余苹果中,每隔两个拿走一个苹果。
    • 也就是从剩余的苹果序列中,每隔三个位置拿走一个苹果。
  3. 每天拿走的苹果数量:
    • 如果剩余的苹果数量为 m,每天拿走的苹果数量 t 为 ( t = \lceil \frac{m}{3} \rceil )。
    • 使用向上取整是因为我们要确保即使剩余苹果数量不能整除 3,也要取走至少一个苹果。
  4. 编号为 n 的苹果:

    • 题目要求计算编号为 n 的苹果在哪一天被拿走。
  5. 编号为 n 的苹果在剩余苹果中的位置:

    • 假设编号为 n 的苹果在剩余苹果 m 中的相对位置为 p
  6. 条件 m % 3 == 1 的解释:

    • 当 m % 3 == 1 时,剩余苹果的数量 m 被 3 除余 1,意味着编号为 n 的苹果位于第一组的第一个位置。
    • 因为每天拿走的都是每组三个苹果中的第一个,所以编号为 n 的苹果位于第一组的第一个位置时,会在当天被拿走。
  7. 判断条件的推导:

    • 如果编号为 n 的苹果被拿走的天数还未记录(day == -1),并且当前剩余苹果数量 m % 3 == 1,那么当天 cnt 就是编号为 n 的苹果被拿走的天数

代码详解

  1. 输入与输出:

    • 使用 freopen 函数读取和输出文件内容(竞赛环境常用)。
    • 读取输入的苹果总数 n
  2. 变量初始化:

    • cnt:记录总共拿走所有苹果所需的天数。
    • day:记录编号为 n 的苹果被拿走的天数。
    • m:当前剩余的苹果数量,初始化为 n
  3. 主循环:

    • 循环条件 while (m) 表示当前还有苹果未被拿走。
    • 每次循环表示新的一天,计数器 cnt 加 1。
  4. 每天拿走的苹果数量计算:

    • t = ceil(m / 3.0) 计算每天拿走的苹果数量,使用 ceil 函数向上取整。
  5. 判断编号为 n 的苹果被拿走的天数:

    • if (m % 3 == 1 && day == -1) 判断编号为 n 的苹果是否在当天被拿走。
    • m % 3 == 1 表示当天编号为 n 的苹果位于第一组。
    • day == -1 保证只记录一次编号为 n 的苹果被拿走的天数。
  6. 更新剩余苹果数量:

    • m -= t 更新当前剩余的苹果数量。
  7. 输出结果:

    • 输出总共拿走所有苹果的天数 cnt 以及编号为 n 的苹果被拿走的天数 day

数学方法的优势

  • 时间复杂度:

    • 由于每次循环中剩余的苹果数量减少了三分之一,时间复杂度是 ( O(\log n) )。
  • 清晰的逻辑:

    • 通过数学规律直接计算每天拿走的苹果数量,避免了复杂的模拟操作。

总结

这个数学方法的核心在于发现每天拿走苹果数量的规律,并通过判断 m % 3 来确定编号为 n 的苹果被拿走的天数。通过这段代码的详细解释,希望你对数学规律的应用有更深入的理解。

  • 10
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值