题解:洛谷 P14073 数字选取

【题目来源】

洛谷:P14073 [GESP202509 五级] 数字选取 - 洛谷

【题目描述】

给定正整数 \(n\),现在有 \(1,2,…,n\) 共计 \(n\) 个整数。你需要从这 \(n\) 个整数中选取一些整数,使得所选取的整数中任意两个不同的整数均互质(也就是说,这两个整数的最大公因数为 \(1\))。请你最大化所选取整数的数量。

例如,当 \(n=9\) 时,可以选择 \(1,5,7,8,9\) 共计 \(5\) 个整数。可以验证不存在数量更多的选取整数的方案。

【输入】

一行,一个正整数 \(n\),表示给定的正整数。

【输出】

一行,一个正整数,表示所选取整数的最大数量。

【输入样例】

6

【输出样例】

4

【核心思想】

  1. 问题分析:从 \(1 \sim n\) 中选取尽可能多的数,使它们两两互质。由于任意两个不同质数一定互质,而 \(1\) 与所有数互质,因此一个自然的想法是:选取所有质数,再额外加上 \(1\)。关键在于证明这是最优的——任何合数都包含至少一个质因子,选择合数就必须放弃它的某个质因子(因为两者不互质),这样的替换不会增加总数,反而可能减少。因此最优策略就是“所有质数 + 1”。

  2. 算法选择

    • 埃氏筛(埃拉托斯特尼筛法):用于统计 \(2 \sim n\) 中质数的个数。由于 \(n \le 10^5\),埃氏筛的 \(O(n \log \log n)\) 复杂度完全足够。
    • 特判 \(n=1\):当 \(n=1\) 时,只能选 \(\{1\}\),答案为 \(1\)
  3. 关键步骤

    • 读入正整数 \(n\)
    • \(n=1\),直接输出 \(1\)
    • 使用埃氏筛标记 \(2 \sim n\) 中的合数,同时统计质数个数 cnt
    • 输出 cnt + 1(加上数字 \(1\))。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n \log \log n)\),埃氏筛的标准复杂度。
    • 空间复杂度:\(O(n)\),用于标记数组。
  5. 质数统计的核心思想

    • 质数的互质性:任意两个不同的质数必然互质,因为它们的唯一正因子是 \(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
posted @ 2026-09-08 20:29  团爸讲算法  阅读(4)  评论(0)    收藏  举报