You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.0 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

P9748 [CSP-J 2023] 小苹果

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

1、看数据范围测试点1 \sim 2n<=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;
}