【练习】洛谷 P3912 素数个数

题目

P3912 素数个数

题目描述

1 , 2 , ⋯   , N 1,2,\cdots,N 1,2,,N 中素数的个数。

输入格式

一行一个整数 N N N

输出格式

一行一个整数,表示素数的个数。

输入输出样例 #1

输入 #1

10

输出 #1

4

说明/提示

对于 40 % 40\% 40% 的数据, 1 ≤ N ≤ 1 0 6 1 \le N \le 10^6 1N106

对于 80 % 80\% 80% 的数据, 1 ≤ N ≤ 1 0 7 1 \le N \le 10^7 1N107

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 1 0 8 1 \le N \le 10^8 1N108

来源:洛谷 P3912 素数个数


思路(注意事项)

使用 int a[N]; 最终不能通过所有测试案例,主要有以下几个方面的原因:

1. 内存占用问题

  • 数组类型导致的内存占用int 类型通常在大多数系统中占用 4 个字节的内存空间。当你定义 int a[N];N = 1e8 + 1 时,数组 a 所占用的内存大小为 4 * (1e8 + 1) 字节,大约为 400MB。
  • 内存限制:在一些在线评测系统(OJ)中,通常会对程序的内存使用量进行限制,一般限制在几百兆字节以内。使用 int 类型的数组会占用大量的内存,很容易超出内存限制,导致程序运行时出错,无法通过所有测试案例。

2. 空间浪费问题

  • 布尔标记本质:在该算法中,数组 a 的作用是标记一个数是否为合数,只需要两种状态,即“是合数”或“是质数”,本质上用一个布尔值(truefalse)就可以表示。
  • int 类型的浪费int 类型有 32 位,可以表示 2 32 2^{32} 232 个不同的值,但在这个场景中,只需要 2 种状态,使用 int 类型会造成大量的空间浪费。

解决方案

使用 bool 类型的数组可以有效解决上述问题:

  • 内存占用小bool 类型通常只占用 1 个字节的内存空间,定义 bool a[N]; 时,数组 a 所占用的内存大小为 (1e8 + 1) 字节,大约为 100MB,相比 int 类型的数组,内存占用减少了四分之三。
  • 满足需求bool 类型正好可以表示“是合数”(true)和“是质数”(false)这两种状态,满足算法的需求。

纯代码

#include<iostream>
using namespace std;

const int N = 1e8 + 1;
bool a[N]; //很关键

int main(){
    int n; 
    cin >> n;

    for (int i = 2; i * i <= n; i++)
        if(a[i] == 0) //很关键
            for (int j = i * i; j <= n; j += i)
                a[j] = 1;

    int sum = 0;
    for (int i = 2; i <= n; i ++)
        if (a[i] == 0) sum ++;
    cout << sum;
    return 0;
}

题解(加注释)

#include<iostream>
using namespace std;

const int N = 1e8 + 1; // 定义数组的最大大小
bool a[N]; // 用于标记是否为合数的数组,a[i] = 0 表示 i 是质数,a[i] = 1 表示 i 是合数

int main(){
    int n; 
    cin >> n; // 输入上限 n

    // 埃拉托斯特尼筛法:标记所有合数
    for (int i = 2; i * i <= n; i++) // 遍历从 2 到 sqrt(n) 的所有数
        if(a[i] == 0) // 如果 i 是质数
            for (int j = i * i; j <= n; j += i) // 标记 i 的所有倍数为合数
                a[j] = 1;

    // 统计质数的数量
    int sum = 0;
    for (int i = 2; i <= n; i ++) // 遍历从 2 到 n 的所有数
        if (a[i] == 0) sum ++; // 如果 i 是质数,计数器加 1

    // 输出质数的数量
    cout << sum;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值