蓝桥杯练习题笔记(三)
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 1≤L≤105。
可以看到题目的数据还是很大的,所以直接用枚举的办法可能时间复杂度上是个大问题。
于是想到得到质数数组其实还有两个比较好的筛数法:埃式筛法、欧拉筛法
我当时看的视频教程:【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,c(a=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 20→10→5→16→8→4→2→1。
根据给定的数字,验证这个猜想,并从最后的 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 1≤n≤100。
本质是一个逆序输出的问题,提到逆序输出我想到两种方法:一个是函数递归、另一个是栈。
这里我直接放官网上人家公开出来写好的代码了
- 递归法:
#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;
}
递归的方法容易想到,但是在细节上要考虑清楚,比如什么时候返回等等
栈的方法就是编写代码相对来说比较容易一点