题解:洛谷 P14073 数字选取
【题目来源】
洛谷:P14073 [GESP202509 五级] 数字选取 - 洛谷
【题目描述】
给定正整数 \(n\),现在有 \(1,2,…,n\) 共计 \(n\) 个整数。你需要从这 \(n\) 个整数中选取一些整数,使得所选取的整数中任意两个不同的整数均互质(也就是说,这两个整数的最大公因数为 \(1\))。请你最大化所选取整数的数量。
例如,当 \(n=9\) 时,可以选择 \(1,5,7,8,9\) 共计 \(5\) 个整数。可以验证不存在数量更多的选取整数的方案。
【输入】
一行,一个正整数 \(n\),表示给定的正整数。
【输出】
一行,一个正整数,表示所选取整数的最大数量。
【输入样例】
6
【输出样例】
4
【核心思想】
-
问题分析:从 \(1 \sim n\) 中选取尽可能多的数,使它们两两互质。由于任意两个不同质数一定互质,而 \(1\) 与所有数互质,因此一个自然的想法是:选取所有质数,再额外加上 \(1\)。关键在于证明这是最优的——任何合数都包含至少一个质因子,选择合数就必须放弃它的某个质因子(因为两者不互质),这样的替换不会增加总数,反而可能减少。因此最优策略就是“所有质数 + 1”。
-
算法选择:
- 埃氏筛(埃拉托斯特尼筛法):用于统计 \(2 \sim n\) 中质数的个数。由于 \(n \le 10^5\),埃氏筛的 \(O(n \log \log n)\) 复杂度完全足够。
- 特判 \(n=1\):当 \(n=1\) 时,只能选 \(\{1\}\),答案为 \(1\)。
-
关键步骤:
- 读入正整数 \(n\)。
- 若 \(n=1\),直接输出 \(1\)。
- 使用埃氏筛标记 \(2 \sim n\) 中的合数,同时统计质数个数
cnt。 - 输出
cnt + 1(加上数字 \(1\))。
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log \log n)\),埃氏筛的标准复杂度。
- 空间复杂度:\(O(n)\),用于标记数组。
-
质数统计的核心思想:
- 质数的互质性:任意两个不同的质数必然互质,因为它们的唯一正因子是 \(1\) 和自身。
- \(1\) 的特殊性:\(1\) 与任何整数都互质,所以总是可以额外选取。
- 合数的劣势:任意合数都至少有一个质因子。若选取一个合数,则必须放弃它的所有质因子(否则不互质),这只会减少可选取的数量,不会带来任何收益。因此最优解中不会包含合数。
- 综上,最大选取数量 = \(1\)(数字 \(1\))+ 质数个数。
【算法标签】
普及- #质数
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义最大范围
int n; // 输入的数字n
int isprime[N]; // 素数标记数组,isprime[i]存储i的最小质因数
map<int, int> mp; // 用于记录已经统计过的质因数
int main()
{
// 输入数字n
cin >> n;
// 埃拉托斯特尼筛法预处理最小质因数
for (int i = 2; i < N; i++)
{
// 如果i是素数,则标记其倍数
if (isprime[i] == 0)
{
for (int j = i + i; j < N; j += i)
{
// 如果j还没有被标记过,则记录其最小质因数
if (isprime[j] == 0)
{
isprime[j] = i;
}
}
}
}
// 统计1到n范围内的素数个数
int cnt = 0;
for (int i = 1; i <= n; i++)
{
if (isprime[i] == 0)
{
cnt++;
}
}
// 统计1到n范围内合数的不同质因数个数
for (int i = 1; i <= n; i++)
{
// 如果是素数或者已经统计过该质因数,则跳过
if (isprime[i] == 0 || mp[isprime[i]] == 1)
{
continue;
}
// 标记该质因数已经统计过
mp[isprime[i]] = 1;
cnt++;
}
// 输出结果:素数个数 + 不同质因数个数
cout << cnt << endl;
return 0;
}
【运行结果】
6
4
浙公网安备 33010602011771号