Files
python/TangDou/CSP-J/J-2023/apple.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

4.0 KiB
Raw Blame History

P9748 [CSP-J 2023] 小苹果

第一题,一般不会用到什么算法,函数什么的都不会用到

1、看数据范围,测试点$1 \sim 2,n<=10$,此时可以打表

 if (n == 1)
        res = 1, day = 1;
    else if (n == 2)
        res = 2, day = 2;
    else if (n == 3)
        res = 3, day = 3;
    else if (n == 4)
        res = 3, day = 1;
    else if (n == 5)
        res = 4, day = 4;
    else if (n == 6)
        res = 4, day = 2;
    else if (n == 7)
        res = 4, day = 1;
    else if (n == 8)
        res = 5, day = 5;
    else if (n == 9)
        res = 5, day = 3;
    else if (n == 10)
        res = 5, day = 1;

用人脑算一下,然后写上去,这就是打表,可以得$20$分。

2、3 \sim 5 \ n<=1000

此时可以开一个$bool$数组,每次遍历$n*n=1000000$次,可以过,又得到$20$分。 当在把$st[n]$设置为$true$时,记录现在是第几天就行了。

#include <bits/stdc++.h>

using namespace std;
const int N = 1000010;
int st[N];

int cnt; // 已经取出的个数
int day; // 第n个苹果是第day天被取走的
int all; // 一共需要多少天

int main() {
    // freopen("apple.in", "r", stdin);
    //   freopen("apple.out", "w", stdout);
    int n;
    cin >> n;

    while (true) {
        // 模拟每一天
        all++;

        int c = 0;
        for (int i = 1; i <= n; i++) {
            if (st[i]) continue;
            c++;
            if (c % 3 == 1) { // 找规律,1,4,7,10...出列
                st[i] = 1;
                cnt++;                 // 又出队一个
                if (i == n) day = all; // 记录n是哪天出队的
            }
        }
        if (cnt == n) break; // 如果出队数量达标就完事
    }
    // 输出一共需要多少天,取出第n号在第几天
    cout << all << " " << day << endl;
    return 0;
}

3、6 \sim 7 $10^6$的数据范围

明显刚才$O(N*N)$的做法不行,太慢了。

此数据有特殊性质:小苞第一天就取走编号为 n 的苹果。

按题意假设有$81$个苹果,实际上变化的是 81->81*2/3=54->54*2/3=36 ->24*2/3=16 -> 16 * 2/3 = ...

你会发现下降的特别快,也就是一次减少2/3,那么$2$次就是减少2/3*2/3=4/9,而4/9<1/2,也就是两次减少$1$半

每次减少$1$半是$log_2{N}$,两次减少一半,就是2*log_2{N},也就是是一个logN 的时间复杂度,并不是O(N*N),是O(2NlogN) 也就是说,上面的代码可以得到$70$分!

4、数学解法

  1. 首先,以3个苹果为一组,每次会取走每一组的第一个,第一次取走苹果的编号为1、4、7.

  2. 第一次取完苹果后,剩余苹果总数和编号如下:

  3. 因为题目不要求输出每次取走苹果的编号,因此,将上述5个苹果重新按照1-5号进行编号,就可以重复之前取苹果的方法,并且,关心的8号苹果,等同于剩余苹果总数第5号苹果什么时候被取走。

#include <bits/stdc++.h>

using namespace std;
int n, all, day;

int main() {
    cin >> n;
    while (n) {                                // 剩余苹果数量
        all++;                                 // 新的一天
        if (day == 0 && n % 3 == 1) day = all; // 判断编号n的苹果第几天被取走
        n -= ceil(1.0 * n / 3);
        // 上取整公式
        // n = n - (n + 2) / 3; // 剩余苹果数
    }
    // 输出总天数 和 第n 个苹果第几天被取走
    cout << all << " " << day << endl;
    return 0;
}