(二十四)进阶算法


经过12天的“魔鬼集训”,我又回来更新了。这篇文章是关于算法的
毕竟集训期间天天讲

(一)埃氏筛法

1. 原理

它是用来筛选质数的,从2开始看(因为1不是质数也不是合数),2是质数,筛选掉所有是2的倍数的数,它们是合数,不知道什么是质数合数点我。随后看下一个没有被筛掉的数:3,再来筛掉,然后看数字5,把数字五的倍数筛掉…

2. 代码

#include <bits/stdc++.h>
using namespace std; 
int a[1001]; //用于记录质数 
bool prime[1001]; //质数筛 
int cnt = 0; //质数的个数
void eldri() {
	fill(prime+2, prime+1001, true); //除1以外,其他的都可能是质数 
	for(int i=2; i<=1000; i++) {
		if(prime[i]) {//判断i有没有被筛掉 
			a[++cnt] = i; 
			for(int j=2*i; j<=1000; j+=i) //筛掉i的倍数 
				prime[j] = false; 
		}
	}
}
int main() {
	ios::sync_with_stdio(false); //加速代码
	cin.tie(0); 
	cout.tie(0); 
	eldri(); //运行函数
	cout << "1~1000中的质数有:"; 
	for(int i=1; i<=cnt; i++) 
		cout << a[i] << " "; 
	cout << endl << "1~1000中质数的个数:" << cnt; 
	return 0; 
}

3. 特点

优点: 比一个个筛质数更好
缺点: 会重复看某个合数
时间复杂度: O ( n l o g l o g n ) O(nloglogn) O(nloglogn)
注意事项: 质数表和质数筛都得放在main()函数外,一是无法访问,二才是段错误
段错误(segmentation fault)可以理解为函数内的变量占用字节太大了,也可能是访问了野指针(指向nullptr或者NULL)或者数组访问越界
毕竟main()也是函数请添加图片描述

(二)欧拉筛法

1. 原理

欧拉筛法的思想是每个合数只被它最小的质数筛掉,比如在看数字 2 2 2时,可以先不考虑筛掉 30 30 30,等到 15 15 15时再筛掉

2. 代码

#include <bits/stdc++.h>
using namespace std; 
bool isp[1001]; //质数表 
int prime[1001]; //质数 
int cnt=0; //质数个数 
void euler() {
	fill(isp+2, isp+1001, true); //除1以外,其他的都可能是质数
	for(int i=2; i<=1000; i++) {
		if(isp[i])
			prime[++cnt] = i; 
		for(int j=1; j<=cnt&&i*prime[j]<=1000; j++) { //质数合数都来筛
			isp[i*prime[j]] = false; //筛掉 
			if(i%prime[j]==0) break; //超出有效范围,退出循环,也是欧拉筛法的关键 
		}
	}
}
int main() {
	ios::sync_with_stdio(false); //加速代码
	cin.tie(0); 
	cout.tie(0); 
	euler(); //运行代码 
	cout << "1~1000间的质数:"; 
	for(int i=1; i<=cnt; i++)
		cout << prime[i] << ' '; 
	cout << endl << "1~1000间质数的个数:" << cnt; 
	return 0; 
}

3. 特点

优点: 不会重复筛数
时间复杂度: O ( n ) O(n) O(n)
注意事项: 分清楚里面的ij

(三)分解质因数

1. 原理

以一个数 84 84 84为例,假如被 2 2 2筛到不能筛时,就不可能被 4 , 6 , 8 4, 6, 8 4,6,8 整除了

2. 代码

#include <iostream>
using namespace std; 
void split(int n) {
	cout << n << '='; 
	int t = 2; //从2开始筛 
	bool flag = false; //是否输出'*' 
	while(n>1) { 
		if(n%t==0) { //n还能整除t
			n /= t; 
			if(flag++) cout << '*'; 
			cout << t; 
		} else { //不可以的话找下一个质数 
			t ++; 
		}
	}
}
int main() {
	int n; 
	cin >> n; 
	split(n); //调用函数 
	return 0; 
}

(四)斐波那契数列

1. 递推式

F i b n = F i b n − 1 + F i b n − 2 Fib_{n}=Fib_{n-1}+Fib_{n-2} Fibn=Fibn1+Fibn2
边界条件: F i b 1 = 1 , F i b 2 = 1 Fib_{1}=1, Fib_{2}=1 Fib1=1,Fib2=1

2. 代码

(1) 方法1
#include <iostream>
#define int unsigned long long //宏定义
using namespace std; 
int fib(int n) {
	if(n==1||n==2) return 1; //边界条件 
	else return fib(n-1)+fib(n-2); //递归式 
}
signed main() { //signed代替int
	int n; 
	cin >> n; 
	cout << fib(n); //调用函数 
	return 0; 
}

但是这样会重复算一些数字
f i b 4 = f i b 3 + f i b 2 = f i b 2 + f i b 1 + f i b 2 = 1 + 1 + 1 = 3 fib_4=fib_3+fib_2 = fib_2+fib_1+fib_2=1+1+1=3 fib4=fib3+fib2=fib2+fib1+fib2=1+1+1=3
这个式子重复计算了 f i b 2 fib_2 fib2
我们可以仿造埃氏筛,做一个用于存储数值的数组

(2) 方法2
#include <iostream>
#define int unsigned long long //宏定义
using namespace std; 
int a[51] = {}; 
int fib(int n) {
	if(n==1||n==2) return 1; //边界条件 
	else if(a[n]>0) return a[n]; //寄存了a[n] 
	else return a[n]=fib(n-1)+fib(n-2); //二合一,表示返回值并赋值 
}
signed main() { //signed代替int
	int n; 
	cin >> n; 
	cout << fib(n); //调用函数 
	return 0; 
}

这样就能解决这个问题了! 请添加图片描述


预览:

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值