蓝桥杯练习题笔记(三)

文章讲解了如何用埃式和欧拉筛法处理质数问题,涉及三角函数的简化技巧,以及如何通过递归和栈实现冰雹猜想的逆序输出。
摘要由CSDN通过智能技术生成

蓝桥杯练习题笔记(三)


1. 质数口袋

题目描述

小 A 有一个质数口袋,里面可以装各个质数。他从 2 2 2 开始,依次判断各个自然数是不是质数,如果是质数就会把这个数字装入口袋。

口袋的负载量就是口袋里的所有数字之和。

但是口袋的承重量有限,装的质数的和不能超过 L L L。给出 L L L,请问口袋里能装下几个质数?将这些质数从小往大输出,然后输出最多能装下的质数的个数,数字之间用换行隔开。

输入格式

一行一个正整数 L L L

输出格式

将这些质数从小往大输出,然后输出最多能装下的质数个数,所有数字之间有一空行。

样例 #1

样例输入 #1

100

样例输出 #1

2
3
5
7
11
13
17
19
23
9

样例 #2

样例输入 #2

5

样例输出 #2

2
3
2

样例 #3

样例输入 #3

11

样例输出 #3

2
3
5
3

提示

数据保证, 1 ≤ L ≤ 10 5 1 \le L \le {10}^5 1L105

可以看到题目的数据还是很大的,所以直接用枚举的办法可能时间复杂度上是个大问题。
于是想到得到质数数组其实还有两个比较好的筛数法:埃式筛法、欧拉筛法
我当时看的视频教程:【C++算法】20分钟学会高效地素数筛法,埃氏筛法,欧拉筛法

  • 埃式筛法原理:
    创建一个布尔数组 primes,并将所有元素初始化为 false。
    从 2 开始遍历到 sqrt(n)(即 i * i <= n),执行以下步骤:
    如果当前的数 i 没有被筛掉(即 isPrime[i] = false),则将其所有倍数标记为非素数。具体操作是从 i * i 开始,每次增加 i,将对应的 primes 值设为 true。
    遍历完所有的数后,数组中值为 false 的索引对应的数即为素数。
  • 欧拉筛法原理:
    创建一个布尔数组 prime,并将所有元素初始化为 false。
    创建一个空的数组 primes,用于存储筛选出的素数。
    从 2 开始遍历到 n,执行以下步骤:
    如果当前的数 i 是素数(即 prime[i] = false),则将其加入 primes 数组中。
    遍历 primes 数组中的每个素数 p,如果 p * i 小于等于 n,则将 p * i 标记为非素数(即 prime[p * i] = true)。
    如果 i 能被 p 整除 ,跳出循环。(这一句是关键,因为要保证 p 是能整除当前数 i 的最小质数,这样才不会重复被筛。比如12 既能被质数2整除,也能被质数3整除,这样就保证了只会被质数2筛一次,不会被3再筛一次)
    遍历完所有的数后,primes 数组中存储的即为所有小于等于 n 的素数。
  • 埃式筛法示例:
#include <iostream>
#include <vector>
#include<cmath>
#include <iomanip>
#include <numeric>
#include <algorithm> 
using namespace std;
#define ll long long

int main()
{
	bool primes[1000005];
	ll n = 1e6;

	for (ll i = 2;i <= n / i ;i++)
	{
		if (!primes[i])
			for (ll j = 2 * i;j <= n;j += i)primes[j] = true;//表示被筛掉了
	}	
	return 0;
}
  • 欧拉筛法示例:
#include <iostream>
#include <vector>
#include<cmath>
#include <iomanip>
#include <numeric>
#include <algorithm> 
using namespace std;
#define ll long long
int n = 1e6;
bool prime[1000005];
int primes[1000005];

int pp = 0;
int main()
{
	int n = 1e6;
	bool prime[1000005];
	int primes[1000005];

	for (int i = 2;i <= n  ;i++)
	{
		if (!prime[i])primes[pp++] = i;
		for (int j = 0;j <= pp;j++)
		{
			prime[primes[j] * i] = true;
			if (i % primes[j] == 0)break;
		}
	}

	return 0;
}

所以上述题目也就能够解决了,其实看了眼别人的答案好像直接枚举算质数好像也行,数据也不大,但有更优秀的算法还是可以学一学的。

  • 示例代码:使用了欧拉筛法得到质数数组,然后再按照题目要求进行输出。
#include <iostream>
#include <vector>
#include<cmath>
#include <iomanip>
#include <numeric>
#include <algorithm> 
using namespace std;
#define ll long long

int main()
{
	bool pri[100005] ;
	for (int i = 2; i <= 100000; ++i)	pri[i] = 0;
	vector<int> primes;
	int n;
	cin >> n;

	for (int i = 2;i < 100000;i++)
	{
		if (!pri[i])primes.push_back(i);
		int pp = primes.size();
		for (int j = 0;primes[j] * i < 100000 &&j < pp;j++)//这里的循环条件注意不要忘了不能让数组越界
		{
			pri[primes[j] * i] = true;
			if (i % primes[j] == 0)break;
		}
	}
	int sum = 0,count=0,i=0;
	while (sum < n)
	{
		if(n<=2)break;
		if (sum + primes[i] > n) break;//这里是为了保证sum在跳出循环时仍然比n小

		sum += primes[i];
		printf("%d\n", primes[i]);
		i++;
		count++;
	}
	
	printf("%d", count);

	
	return 0;

}

注意循环边界的检查:这里得保证sum在跳出循环时仍然小于n,所以要在前面加一个if来进行控制。(ex了我好久,一直以为是其他地方的问题)
注意不要忘了不能让数组越界

2. 三角函数

题目描述

输入一组勾股数 a , b , c ( a ≠ b ≠ c ) a,b,c(a\neq b\neq c) a,b,ca=b=c,用分数格式输出其较小锐角的正弦值。(要求约分。)

输入格式

一行,包含三个正整数,即勾股数 a , b , c a,b,c a,b,c(无大小顺序)。

输出格式

一行,包含一个分数,即较小锐角的正弦值

样例 #1

样例输入 #1

3 5 4

样例输出 #1

3/5

提示

数据保证: a , b , c a,b,c a,b,c 为正整数且 ∈ [ 1 , 1 0 9 ] \in [1,10^9] [1,109]

问题不复杂,只是有个点注意一下,那就是约分:核心问题就是求最大公约数,通常使用辗转相除法来做

int gcd(int x, int y)//要求x>=y
{
    if (y == 0) return x;
    return gcd(y, x % y);
}

3. 冰雹猜想

题目描述

给出一个正整数 n n n,然后对这个数字一直进行下面的操作:如果这个数字是奇数,那么将其乘 3 3 3 再加 1 1 1,否则除以 2 2 2。经过若干次循环后,最终都会回到 1 1 1。经过验证很大的数字( 7 × 1 0 11 7\times10^{11} 7×1011)都可以按照这样的方式比变成 1 1 1,所以被称为“冰雹猜想”。例如当 n n n 20 20 20,变化的过程是 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 20\to 10\to 5\to 16\to 8\to 4\to 2\to 1 20105168421

根据给定的数字,验证这个猜想,并从最后的 1 1 1 开始,倒序输出整个变化序列。

输入格式

输入一个正整数 n n n

输出格式

输出若干个由空格隔开的正整数,表示从最后的 1 1 1 开始倒序的变化数列。

样例 #1

样例输入 #1

20

样例输出 #1

1 2 4 8 16 5 10 20

提示

数据保证, 1 ≤ n ≤ 100 1 \le n\le 100 1n100

本质是一个逆序输出的问题,提到逆序输出我想到两种方法:一个是函数递归、另一个是栈。
这里我直接放官网上人家公开出来写好的代码了

  • 递归法:
#include<bits/stdc++.h>
using namespace std;
void write(int n){
	if(n==1){
		putchar('1');
		return;
	}
	if(n&1)write(n*3+1);
	else write(n>>1);
	printf(" %d",n);
}
int main(){
	int n;
	scanf("%d",&n);
	write(n);
    return 0;
}
  • 栈:
#include<bits/stdc++.h>
#include<stack>
using namespace std;
stack<int> s;
int main()
{
    int n;
    cin>>n;
    while(n!=1)
    {
        s.push(n);
        if(n%2==0)
        {
        	n/=2;
		}
		else
		{
			n=n*3+1;
		}
    }
    s.push(1);
    while(!s.empty())
    {
    	cout<<s.top()<<" ";
    	s.pop();
	}
    return 0;
}

递归的方法容易想到,但是在细节上要考虑清楚,比如什么时候返回等等
栈的方法就是编写代码相对来说比较容易一点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值