• 洛师ACM每周集训 第四周 素数专题 | HDU 2136 | HDU 1239 | HDU 1397 | HDU 1016 | HDU 5750 | HDU 2012 | HDU 2098 | HDU 2098 | HDU1262


    A - 寻找素数对

    每个偶数都能找到两个素数,其相加等于该数

    简单推一下比如 12 从中间 6 开始, 5 + 7 便是

    再推几个就可以发现
    只要从中间开始找,左右对称的第一个素数对便是要找的

    所以我们抓到了关键点,需要 判断是否为素数 + 从中间开始往两边对称查找

    注意如果中间的素数*2 就成立的话那也算

    #include <cstdio>
    const int  N = 1e4 + 10;
    bool st[N];
    int prime[N], cnt;
    int a[N];
    int main()
    {
    	int n;
    	for (int i = 2; i <= N; i++)
    	{
    		if (!st[i])
    		{
    			a[i] = i; // 查表
    			prime[cnt++] = i;
    		}
    		for (int j = 0; prime[j] * i <= N; j++) // 欧拉筛枚举素数
    		{
    			st[i * prime[j]] = true;
    			if (i % prime[j] == 0) break;
    		}
    		//printf("%d ", a[i]);
    	}
    	while (~scanf("%d", &n))
    	{
    		int t = n / 2;
    		while (t) // 从中间开始左右枚举
    		{
    			if (a[t])
    				if (a[n - t])
    				{
    					printf("%d %d\n", a[t], a[n - t]);
    					break;
    				}
    			t--;
    		}
    
    	}
    }
    

    B - 素数回文 HDU 1431

    这题多考了个判断回文数

    第一次做用的定义字符数组,将每位数存进去
    然后双指针前后一起查找判断是否相等,结果超时了

    后来查了别人博客发现可以直接定义一个int然后通过 b = b * 10 + (a % 10)
    来直接得到逆序数字,之后判断一次相等就行

    #include <cstdio>
    const int N = 9989999;
    bool st[N];
    int prime[N], cnt;
    int a[N];
    
    
    bool iscircle(int x)
    {
    	int b = 0;
    	int a = x;
    	while (a)
    	{
    		b = a % 10 + b * 10;
    		a /= 10;
    	}
    	if (x == b) return true;
    	return false;
    }
    
    void Euler()
    {
    	for (int i = 2; i <= N; i++)
    	{
    		if (!st[i])
    		{
    			if (iscircle(i)) a[cnt] = i;
    			prime[cnt++] = i;
    		}
    		for (int j = 0; prime[j] * i <= N; j++)
    		{
    			st[i * prime[j]] = true;
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    
    int main()
    {
    	Euler(); // 初始化 a 查询数组
    
    	int num1, num2;
    	while (~scanf("%d %d", &num1, &num2))
    	{
    		if (num2 >= 9989899) num2 = 9989899; 
    		// 观察数据可以知道,最大就是这个
    		for (int i = 0; a[i] <= num2 && i <= 663960; i++)
    		{
    			//if (a[i] == 9989899)
    			//	printf(" i = %d\n",i);
    			if (a[i] >= num1) printf("%d\n", a[i]);
    		}
    		printf("\n");
    	}
    }
    

    C - 分拆素数和 HDU 2098

    水题,跟 A 一样做就行,不过是把输出变为 res++

    不过这个题中两个素数不能相同

    #include<cstdio>
    const int N = 1e4 + 10;
    bool st[N];
    int primes[N], cnt;
    int a[N];
    
    void getprime()
    {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			a[i] = i;
    			primes[cnt++] = i;
    		}
    
    		for (int j = 0; primes[j] * i <= N; j++) {
    			st[i * primes[j]] = true;
    			if (i % primes[j] == 0) break;
    		}
    	}
    }
    
    int main() {
    	int n;
    	getprime();
    	while (~scanf("%d", &n) && n != 0) {
    		int t = n / 2;
    		int ans = 0;
    		while (t) {
    			if (a[t]) {
    				if (a[n - t]) {
    					if (a[t] != a[n - t])
    						ans++;
    				}
    			}
    			t--;
    		}
    		printf("%d\n", ans);
    	}
    }
    

    D - 素数判定 HDU 2012


    how waterful

    #include<cstdio>
    const int N = 1e6 + 10;
    bool st[N];
    int primes[N], cnt;
    int a[N];
    
    void getprime()
    {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			a[i] = i;
    			primes[cnt++] = i;
    		}
    
    		for (int j = 0; primes[j] * i <= N; j++) {
    			st[i * primes[j]] = true;
    			if (i % primes[j] == 0) break;
    		}
    	}
    }
    
    int main() {
    	int x, y;
    	getprime();
    	while (~scanf("%d%d", &x, &y) && (x || y)) {
    		bool flag = true;
    		for (int i = x; i <= y; i++)
    		{
    			int res = i * i + i + 41;
    			if (!a[res]) {
    				flag = false;
    				break;
    			}
    
    		}
    		if (flag) printf("OK\n");
    		else printf("Sorry\n");
    	}
    
    

    E - Dertouzos HDU 5750

    这题硬核一些
    题目要求找到 从 1~ n 中 以 d 为最大因子的有几个


    12 : 1 2 3 4 6 12,最大因子为 6
    9: 1 3 9 最大因子为 3
    6:1 2 3 6 最大因子为 3
    而我们已知的是最大因子 d,需要求出最大因子为 d 的数 a
    那他跟 a 有啥关系? 对,因子!
    12 = 2 * 6
    9 = 3 * 3
    6 = 2 * 3
    似乎有什么相似之处,都是与左边第一个非 1 因子相乘

    而一个数左边的最开始的因子一定符合什么性质? 是质数

    诶,那我是不是用 d 跟素数表从小到大乘以便就能找出 所有以 d 为最大因子的数呢?

    d = 4 时比如 3 * 4 = 12,也是跟素数相乘啊,但此时最大因子不再是 4,而是6
    所以需要一个条件来约束,不能从头乘到尾
    prime[i] % d != 0

    总结一下
    读入 d,用 d 跟素数表从小到大依次相乘,当碰到自己的因子时就停止

    #include<cstdio>
    #include <cstdlib>
    const int N = 1e6 + 10;
    bool st[N];
    int primes[N], cnt;
    //int a[N];
    
    void getprime()
    {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			//a[i] = i;
    			primes[cnt++] = i;
    		}
    
    		for (int j = 0; primes[j] * i <= N; j++) {
    			st[i * primes[j]] = true;
    			if (i % primes[j] == 0) break;
    		}
    	}
    }
    
    int main() {
    	getprime();
    	int n, d;
    	int T;
    	scanf("%d", &T);
    	while (T--) {
    		int ans = 0;
    		scanf("%d%d", &n, &d);
    		for (int i = 0; i < cnt; i++) {
    			if (primes[i] * d > n) break; // 当大于 n 时超出范围结束 
    			ans++;
    			if (d % primes[i] == 0) break; // 该结束了
    		}
    		printf("%d\n", ans);
    	}
    	return 0;
    }
    
    

    F - Prime Ring Problem HDU 1016

    你以为是考素数?
    是考我 DFS(深度优先搜索)哒!

    没学咋办?学啊!

    假设 n = 6, 则共有这几个数 1 2 3 4 5 6
    从 1 开始,下一位 共可以放 2 3 4 5 6,但成立的只有 2 4 6
    枚举时不成立的直接回溯
    成立的就继续往下走

    假设 1
    选了 2, 那么当前序列 1 2, 可以选 3 4 5 6,成立的有 3 5
    选了 3, 那么当前序列 1 2 3, 可以选 4 5 6, 成立的有 4
    选了 4, 那么当前序列 1 2 3 4,可以选 5 6,成立的有 寄

    回溯到 选 2
    选了 5,那么当前序列 1 2 5, 可以选 3 4 6, 成立的有 6
    选了 6, 那么当前序列 1 2 5 6,可以选 3 4, 成立的有 寄
    回溯到起点

    假设2
    选 4, 那么当前序列 1 4,可以选 2 3 5 6, 成立的有 3
    选 3, 那么当前序列 1 4 3,可以选 2 5 6, 成立的有 2
    选 2, 那么当前序列 1 4 3 2, 可以选 5 6,成立的有 5
    选 5, 那么当前序列 1 4 3 2 5,可以选 6,成立的有 6
    选 6, 那么当前序列 1 4 3 2 5 6,到头,判断一下 1 + 6 是否为质数,成立
    所以有一个正确答案 1 4 3 2 5 6

    先贴一个dfs的模板,理解理解

    // 输入一个数n,求出从1-n所有数的组合
    // 输入 3
    // 输出 123 132 213 231 312 321 (随便打的)
    #include <cstdio>
    const int N = 100020;
    int path[N], u, n;
    bool st[N];
    
    void dfs(int u) {
        if (u == n) { // 如果当前深搜层数到底,则输出当前路径的结果
            for (int i = 0; i < n; i++) printf("%d ", path[i]);
            printf("\n");
            return;
        }
        for (int i = 1; i <= n; i++) { // 枚举每个可能项
            if (!st[i]) {                       
    			// 如果当前项已被枚举过则跳过
                path[u] = i;              
    			//  当前路径层赋予结果
                st[i] = true;              
    			//  当前项已被枚举
                dfs(u + 1);                
    			// 下一层搜索
                st[i] = false;              
    			//  回溯,因为路径中每次都会重新赋值,所以不必回溯路径
            }
        }
    }
    
    int main() {
        scanf("%d", &n);
        dfs(0);  // 从第0层深搜开始
        return 0;
    }
    
    

    这个理解完了再看代码就很容易了

    /*
    Problem link : https://vjudge.net/problem/HDU-1016
    Finish time : 2021/12/2 17:30 made by Aze
    */
    
    #include <iostream>
    #include <cstring>
    const int N = 1e6 + 10;
    bool st[N]; 
    int primes[N], cnt;
    int a[N]; // 前三行都是素数的东西
    
    int path[N], n; // DFS
    
    void getprime()
    {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			a[i] = i;
    			primes[cnt++] = i;
    		}
    
    		for (int j = 0; primes[j] * i <= N; j++) {
    			st[i * primes[j]] = true;
    			if (i % primes[j] == 0) break;
    		}
    	}
    }
    void dfs(int u, int num) {
    	if (u == n - 1 && a[path[u - 1] + 1]) { // 判断是否到达底层
    		printf("1 ");
    		for (int i = 0; i < n - 1; i++) {
    			printf("%d%c", path[i], i == n - 2 ? '\n' : ' '); 
    			// 输出的行末不能有空格
    		}
    	}
    	for (int i = 2; i <= n; i++) { // 从2开始枚举
    		if (!st[i]) {			// 如果没被枚举过就继续
    			if (a[num + i]) { //如果跟上一个数相加为素数
    				path[u] = i; // 放进结果里
    				st[i] = true; // 标记当前数已经被枚举过
    				dfs(u + 1, i);
    				st[i] = false;
    			}
    		}
    	}
    }
    
    
    
    
    int main() {
    	getprime();
    	cnt = 1;
    	memset(st, false, sizeof(st));
    	while (~scanf("%d", &n)) {
    		printf("Case %d:\n", cnt);
    		dfs(0, 1);
    		cnt++;
    		printf("\n");
    	}
    	return 0;
    }
    

    G - Goldbach's Conjecture HDU 1397

    属于是英文版C题了
    不过这次可以两个素数相同

    #include <iostream>
    using namespace std;
    const int N = 1e6 + 10;
    bool st[N];
    int prime[N], cnt;
    int a[N];
    
    void getprime() {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			a[i] = i;
    			prime[cnt++] = i;
    		}
    		for (int j = 0; prime[j] * i <= N; j++) {
    			st[prime[j] * i] = true;
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    
    int main() {
    	int n;
    	getprime();
    	while (~scanf("%d", &n) && n) {
    		int res = 0;
    		int t = n / 2;
    		while (t) {
    			if (a[t]) {
    				if (a[n - t]) {
    					res++;
    				}
    			}
    			t--;
    		}
    		printf("%d\n", res);
    	}
    }
    

    H - Calling Extraterrestrial Intelligence Again HDU 1239

    全是废话,建议从 In other words 开始看
    输入数 m a b,其中 4 < m <= 1e5 1 <= a <= b <= 1000
    找出两个素数 q p,满足 pq <= m,且 a / b <= p / q <= 1
    输出满足条件的最大 q p

    那就直接从m中间左右开找,全遍历
    优化一下,从素数表里找

    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 1e5 + 10;
    int prime[N], cnt;
    bool st[N];
    
    void getprime() {
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) prime[cnt++] = i;
    		for (int j = 0; prime[j] * i <= N; j++) {
    			st[i * prime[j]] = true;
    			if (i % prime[j] == 0) break;
    		}
    	}
    }
    
    
    int find(int x) { // 二分找
    	int l = 0, r = cnt, mid;
    	while (l < r) {
    		mid = l + r >> 1;
    		if (prime[mid] >= x) r = mid;
    		else l = mid + 1;
    	}
    	return r;
    }
    
    
    int main() {
    	getprime();
    	int m, a, b;
    	while (~scanf("%d%d%d", &m, &a, &b) && m + a + b) {
    		int idx = find(m); // 找到小于等于m的最大素数
    		double c = (double)a / (double)b;
    		int p, q;
    		int max1 = -2e9;
    		for (int i = idx; i >= 0; i--)
    			for (int j = i; j <= idx; j++) {
    				if (prime[i] * prime[j] > m 
    				|| (double)prime[i] / (double)prime[j] < c) break;
    				if (prime[i] * prime[j] > max1) {
    					max1 = prime[i] * prime[j];
    					p = i;
    					q = j;
    				}
    			}
    		printf("%d %d\n", prime[p], prime[q]);
    	}
    }
    

    I - Largest prime factor HDU 2136

    找出一个数的最大素数因子,并输出它在素数表中的位置

    比如 5 的最大素数因子 是 5, 5在素数表里位置为 3
    12 的最大素数因子 为 3,位置为2

    emmm,这题也好做,从 n 开始逆向遍历,判断第一个素数并且是它因子的就行
    但感觉会TLE,在素数表里查应该也行

    欧拉筛是碰到最小素因子就结束,
    比如12,当枚举到6时,6 * 2 就结束了,枚举不到 3

    但还有埃氏筛嘛,它会把是这个素因子的数全标记一遍,那么如果是后面的素数
    也会重新把数标记一遍,直接替换掉原来的标记,这样最后得到的就是最大的了
    还是12, 素数2会标记一次,3又会标记一次

    代码如下:

    #include <iostream>
    const int N = 1e6 + 10;
    int prime[N], cnt = 1; // 位置从 1 开始
    bool st[N];
    int a[N];
    
    void  getprime() {
    	a[1] = 0; // 特殊条件
    	for (int i = 2; i <= N; i++) {
    		if (!st[i]) {
    			a[i] = cnt;			// 素数位置记录
    			prime[cnt] = i;
    			for (int j = i + i; j <= N; j += i) {
    				st[j] = true;
    				a[j] = cnt; // 这个数 的最大素因子(暂时)的位置在 cnt
    			}
    			cnt++;
    		}
    	}
    }
    
    int main() {
    	getprime();
    	int n;
    	while (~scanf("%d", &n)) {
    		printf("%d\n", a[n]);
    	}
    
    }
    

    J - How many prime numbers HDU 2138

    休息休息
    数据很大,不能查表
    试除法搞定
    不过边界条件别用 i * i <= n,会 WA,用 sqrt(n * 1.0)

    #include <iostream>
    #include <cmath>
    
    const int N = 1e7;
    
    bool isprime(int x) {
    	for (int i = 2; i <= (int)sqrt(x * 1.0); i++) {
    		if (x % i == 0) return false;
    	}
    	return true;
    }
    
    int main() {
    	int n;
    	while (~scanf("%d", &n)) {
    		int res = 0;
    		for (int i = 0; i < n; i++) {
    			int temp;
    			scanf("%d", &temp);
    			if (isprime(temp))
    				res++;
    		}
    		printf("%d\n", res);
    	}
    
    }
    
    
  • 相关阅读:
    Linux系统-命令行快捷键
    Golang理解-Context包
    Golang理解-垃圾回收机制
    Linux系统-Systemd资源控制
    Linux系统-"cannot access Input/output error"
    Linux系统-ntpdate时间同步报错
    Golang理解-数组和切片
    Golang理解-位运算
    Golang理解-指针
    Maven配置,使用IntelliJ IDEA和Maven创建Java Web项目
  • 原文地址:https://www.cnblogs.com/edwinaze/p/15668485.html
Copyright © 2020-2023  润新知