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.

120 lines
4.0 KiB

2 years ago
### [$P9748 [CSP-J 2023]$ 小苹果](https://www.luogu.com.cn/problem/P9748?contestId=140858)
第一题,一般不会用到什么算法,函数什么的都不会用到
#### 1、看数据范围测试点$1 \sim 2n<=10$,此时可以打表
```cpp {.line-numbers}
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$时,记录现在是第几天就行了。
```cpp {.line-numbers}
#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.
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202311021156219.png)
2. 第一次取完苹果后,剩余苹果总数和编号如下:
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202311021156743.png)
3. 因为题目不要求输出每次取走苹果的编号因此将上述5个苹果重新按照1-5号进行编号就可以重复之前取苹果的方法并且关心的8号苹果等同于剩余苹果总数第5号苹果什么时候被取走。
![](https://dsideal.obs.cn-north-1.myhuaweicloud.com/HuangHai/BlogImages/202311021157150.png)
```cpp {.line-numbers}
#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;
}
```