51Nod 2456 最小约数 V2 质数

1. 题目描述

1.1. Limit

Time Limit: 1000 ms

Memory Limit: 131072 kB

1.2. Problem Description

给出 n n 个数,将这个 n n 数进行分组,分组规则为:除了1以外最小的约数相同的数字分为一组。最后,输出这个最小约数,同时按照从小到大的顺序逐一输出这些数字。

例如:7个数,35 8 39 12 8 26 25
输出为:

2 8 8 12 26( [8, 8, 12, 26] 为给定的7个数中,以2为最小约数(除1以外)的数)
3 39( [39] 为给定的7个数中,以3为最小约数(除1以外)的数)
5 25 35( [25, 35] 为给定的7个数中,以5为最小约数(除1以外)的数)


1.3. Input

第一行:1个数 n n 1000 n(n \le 1000)
后面 n n 行,每行1个数 a [ i ] 2 < = a [ i ] < = 10000 a[i](2 <= a[i] <= 10000)


1.4. Output

按照约数 d d 从小到大的顺序,逐行输出所有最小约数(除了1之外的)为 d d 的数字,并且每行中输出数字的顺序也是从小到大。


1.5. Sample Input

7
35
8
39
12
8
26
25

1.6. Sample Output

2 8 8 12 26
3 39
5 25 35

1.7. Source

51Nod 2456 最小约数 V2


2. 解读

这道题有两个部分需要完成

  1. 分解最小约数
  2. 分组排序输出

首先我们考虑分解最小约数的问题。

给定一个数 x x ,要找到其除了1以外最小的约数 d d ,以此可以推断我们要找的这个约数肯定是一个质数,因为如果 d d 不是一个质数,那么我们只要对其再做一次质数分解,就可以求得比它更小的一个约数。

明确了我们要找的数是一个质数以后,我们可以考虑在求出质数表以后用暴力搜索的方法来求解。结合这道题的数据范围 [ 1 , 10000 ] [1, 10000] 考虑,在这个范围里面只有1229个质数,而输入也最多只有1000个数, 1229 × 1000 = 1.229 × 1 0 6 1229 \times 1000 = 1.229 \times 10 ^6 ,也就是说在求出一万以内的质数以后,我们最多只要再做100多万次运算就能求出结果。

而我们使用的求质数的埃氏筛算法的复杂度也是 O ( n log log n ) O(n \log\log n) 的,接近 O ( n ) O(n) 复杂度,所以和搜索加起来总共也就一百多万次运算。参考一台普通的笔记本电脑1秒大概1千万次的运算速度,我们是可以在1秒以内完成暴力搜索过程的。

下面对埃氏筛算法做简单的说明,这个算法可以快速找到 [ 2 , n ] [2, n] 以内的质数。对于初始队列 { 1 , 2 , 3 , n } \{1, 2, 3, \dots n \} ,操作步骤如下 1

  1. 输出最小的素数2,然后筛掉2的倍数,剩下 { 3 , 5 , 7 , 9 , 11 , 13 } \{ 3, 5, 7, 9, 11, 13\dots \}
  2. 输出最小的质数3,然后筛掉3的倍数,剩下 { 5 , 7 , 11 , 13 } \{5, 7, 11, 13\dots \}
  3. 输出最小的质数5,然后筛掉5的倍数,剩下 { 7 , 11 , 13 } \{ 7, 11, 13\dots \}
  4. 继续以上的步骤,直到队列为空

然后考虑分组排序输出的问题。

我使用的是 map<int, multiset<int>> 来存储结果,因为 map 能对 key 也就是最小约数进行排序,而 set 能对值排序,考虑到值可能重复,这里使用的是可存储重复项的 multiset

3. 代码

#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <string.h>

using namespace std;

const int MAXN = 1e4;

// 存储素数
int prime[MAXN + 1];
// 标记访问
bool visit[MAXN + 1];
// 分类存储
map<int, multiset<int>> mp;

// 埃氏筛法,计算[2, n]内的素数
int E_sieve(int n)
{
    // 初始化
    memset(visit, 0, sizeof(visit));
    // 筛掉非素数
    for (int i = 2; i * i <= n; i++) {
        if (!visit[i]) {
            for (int j = i * i; j <= n; j += i) {
                // 标记为非素数
                visit[j] = true;
            }
        }
    }
    // 下面记录素数
    // 统计素数的个数
    int k = 0;
    for (int i = 2; i <= n; i++) {
        if (!visit[i]) {
            prime[k++] = i;
        }
    }
    return k;
}

int main()
{
    // 计算1e4以内的素数
    int primeMark = E_sieve(MAXN);
    // test case
    int t;
    scanf("%d", &t);
    // buffer
    int buffer;

    // 循环
    for (int i = 0; i < t; i++) {

        // 输入
        scanf("%d", &buffer);
        // 找到其质数分解后,除1以外的的最小质数
        int j = 0;
        // 若质数小于buffer
        while (prime[j] <= buffer && j < primeMark) {
            // 找到的第一个质数
            if (buffer % prime[j] == 0) {
                if (mp.find(prime[j]) != mp.end()) {
                    // 若已存储过
                    mp[prime[j]].insert(buffer);
                } else {
                    // 若未存储过
                    multiset<int> st;
                    st.insert(buffer);
                    mp[prime[j]] = st;
                }
                // 退出循环
                break;
            }
            j++;
        }
    }
    // 输出
    for (auto it = mp.begin(); it != mp.end(); it++) {
        printf("%d ", it->first);
        for (auto itSet = it->second.begin(); itSet != it->second.end(); itSet++) {
            printf("%d ", *itSet);
        }
        printf("\n");
    }
    return 0;
}

4. 参考文献

[1] 罗勇军, 郭卫斌. 算法竞赛入门到进阶 [M]. 北京: 清华大学出版社, 2019.


联系邮箱:[email protected]

Github:https://github.com/CurrenWong

欢迎转载/Star/Fork,有问题欢迎通过邮箱交流。

猜你喜欢

转载自blog.csdn.net/qq_41729780/article/details/105862364