docs(B4050): 添加 GESP202409 五级挑战怪物题解- 新增挑战怪物问题的题解文档

- 实现了埃氏筛法来生成质数
This commit is contained in:
sairate 2025-06-01 18:36:41 +08:00
commit 29046fc289
9 changed files with 211 additions and 0 deletions

3
.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

View File

@ -0,0 +1,14 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoredPackages">
<value>
<list size="1">
<item index="0" class="java.lang.String" itemvalue="numpy" />
</list>
</value>
</option>
</inspection_tool>
</profile>
</component>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

12
.idea/material_theme_project_new.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="21c1c7ee:193388d497c:-7ff9" />
</MTProjectMetadataState>
</option>
</component>
</project>

7
.idea/misc.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13" project-jdk-type="Python SDK" />
</project>

8
.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/题目.iml" filepath="$PROJECT_DIR$/.idea/题目.iml" />
</modules>
</component>
</project>

8
.idea/题目.iml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -0,0 +1,153 @@
# B4050 [GESP202409 五级] 挑战怪物
## 题目描述
小杨正在和一个怪物战斗,怪物的血量为 $h$,只有当怪物的血量**恰好**为 $0$ 时小杨才能够成功击败怪物。
小杨有两种攻击怪物的方式:
- 物理攻击。假设当前为小杨第 $i$ 次使用物理攻击,则会对怪物造成 $2^{i - 1}$ 点伤害。
- 魔法攻击。小杨选择任意一个质数 $x$ 不能超过怪物当前血量),对怪物造成 $x$ 点伤害。由于小杨并不擅长魔法,他只能使用**至多一次**魔法攻击。
小杨想知道自己能否击败怪物,如果能,小杨想知道自己最少需要多少次攻击。
## 输入格式
**本题单个测试点内有多组测试数据**。第一行包含一个正整数 $t$,代表测试用例组数。
接下来是 $t$ 组测试用例。对于每组测试用例,只有一行一个整数 $h$,代表怪物血量。
## 输出格式
对于每组测试用例,如果小杨能够击败怪物,输出一个整数,代表小杨需要的最少攻击次数,如果不能击败怪物,
输出 $-1$。
## 输入输出样例 #1
### 输入 #1
```
3
6
188
9999
```
### 输出 #1
```
2
4
-1
```
## 说明/提示
### 样例 1 解释
对于第一组测试用例,一种可能的最优方案为,小杨先对怪物使用魔法攻击,选择质数 $5$ 造成 $5$ 点伤害,之后对怪
物使用第 $1$ 次物理攻击,造成 $2^{1 - 1} = 1$ 点伤害,怪物血量恰好为 $0$,小杨成功击败怪物。
### 数据规模与约定
| 子任务编号 | 分数占比 | $t$ | $h$ |
| :-: | :-: | :-: | :-: |
| $1$ | $20\%$ | $\leq 5$ | $\leq 10$ |
| $2$ | $20\%$ | $\leq 10$ | $\leq 100$ |
| $3$ | $60\%$ | $\leq 10$ | $\leq 10^5$ |
对于全部的测试数据,保证 $1 \leq t \leq 10$$1 \leq h \leq 10^5$。
```cpp
#include <iostream>
#include <vector>
using namespace std;
#define int long long // 使用 long long 防止溢出(虽然本题范围不大,但保险起见)
const int MAX_N = 100000; // 最大血量,质数筛范围
// 使用埃氏筛法筛出所有不超过 MAX_N 的质数
vector<int> get_primes(int limit) {
vector<bool> is_prime(limit + 1, true); // 标记数组,默认都为质数
is_prime[0] = is_prime[1] = false; // 0 和 1 不是质数
// 从 2 开始筛除合数
for (int i = 2; i * i <= limit; ++i) {
if (is_prime[i]) {
for (int j = i * i; j <= limit; j += i)
is_prime[j] = false; // 把 i 的倍数都标记为合数
}
}
// 收集所有标记为 true 的质数
vector<int> primes;
for (int i = 2; i <= limit; ++i) {
if (is_prime[i])
primes.push_back(i);
}
return primes;
}
// 判断一个数是否是 2^k - 1 形式的数即全是1的二进制比如 1, 3, 7, 15, 31, ...
bool is_pow2_minus1(int x) {
return x > 0 && ((x + 1) & x) == 0;
}
// 计算一个数的二进制中有多少个 1即物理攻击的次数
int countOnes(int x) {
int count = 0;
while (x) {
x &= (x - 1); // 每次消去最低位的 1
count++;
}
return count;
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 预处理出所有不超过 MAX_N 的质数
vector<int> primes = get_primes(MAX_N);
int t;
cin >> t; // 读入测试数据组数
vector<int> results; // 存储每组答案
while (t--) {
int h;
cin >> h; // 当前怪物的血量
int res = -1; // 初始化结果为 -1表示默认无法击败怪物
// 尝试使用一次魔法攻击 + 若干次物理攻击
for (int i = primes.size() - 1; i >= 0; --i) {
if (primes[i] > h) continue; // 魔法攻击不能超过当前血量
if (primes[i] == h) {
res = 1; // 魔法攻击刚好等于血量1 次攻击即可
break;
}
int remain = h - primes[i]; // 使用魔法后剩余血量
if (is_pow2_minus1(remain)) {
// 如果剩余血量是 2^k - 1就能被若干次物理攻击刚好打掉
int total_attacks = 1 + countOnes(remain); // 1 次魔法 + 物理攻击次数
if (res == -1 || total_attacks < res)
res = total_attacks;
// 不 break继续尝试其他可能更优的魔法攻击组合
}
}
// 如果上面没有找到合适的魔法组合,再尝试只用物理攻击
if (res == -1 && is_pow2_minus1(h)) {
res = countOnes(h); // 全靠物理攻击,计算最少次数
}
results.push_back(res); // 保存当前结果
}
// 输出所有测试结果
for (int res : results) {
cout << res << endl;
}
return 0;
}
```