【PAT乙级】数学问题常用模板(c++)

20220301更新:分数四则运算写法可以再简化

1.最大公约数

(1)欧几里得算法(即辗转相除法)

int gcd(int a, int b)
{
	return !b ? a : gcd(b, a % b);
}

2.最大公倍数

int lcm(int a, int b)
{
	int d = gcd(a, b);
	return a / d * b; //不写成a * b / d 是为了防止数据溢出
}

3. 分数的四则运算

(1)《算法笔记》中的方法(定义结构体)

a. 定义结构体Fraction

struct Fraction
{
	ll up, down; //用long long储存防止乘除时数据溢出
}m, n;

b. 将分数化简

Fraction simpli(Fraction a)
{
	//1.if(down < 0), up & down 同取相反数(保证负号只在分子前)
	//2.约定如果up = 0, 则down = 1 (20220301更新:不要这一步也不影响正确性)
	//3.约分
	if (a.down < 0)
	{
		a.down *= -1;
		a.up *= -1;
	}
	if (a.up == 0)
	{
		a.down = 1;
	}
	else
	{
		int d = gcd(abs(a.up), abs(a.down)); 
		a.up /= d;
		a.down /= d;
	}

	return a;
}

c. 将结果以k a/b形式输出,负数需要加括号

void showRes(Fraction a)
{
	//1. 化简simpli()
	//2.if(down == 1), return up;
	//3.else if(abs(up) > down),假分数
	//4.else 真分数
	a = simpli(a);
	int flag = 0;
	if (a.up < 0)
	{
		cout << "(-";
		a.up *= -1;
		flag = 1;
	}
	if (a.down == 1)
		printf("%lld", a.up);
	else if (a.up > a.down) //20220301 abs(a.up) -> a.up
		printf("%lld %lld/%lld", a.up / a.down, a.up % a.down, a.down);
	else
		printf("%lld/%lld", a.up, a.down);
	if (flag)
		cout << ')';

}

d. 四则运算的函数add()/minu()/multi()/divide()

Fraction add(Fraction a, Fraction b)
{
	Fraction res;
	res.up = a.up * b.down + b.up * a.down;
	res.down = a.down * b.down;
	return simpli(res);

}

Fraction minu(Fraction a, Fraction b)
{
	Fraction res;
	res.up = a.up * b.down - b.up * a.down;
	res.down = a.down * b.down;
	return simpli(res);
}

Fraction multi(Fraction a, Fraction b)
{
	Fraction res;
	res.up = a.up * b.up;
	res.down = a.down * b.down;
	return simpli(res);
}

Fraction divide(Fraction a, Fraction b)
{
	Fraction res;
	res.up = a.up * b.down;
	res.down = a.down * b.up;
	return simpli(res);
}

4.素数

(1)如何判断给定正整数n是否为素数:bool isPrime(int n)

bool isPrime(int n)
{
	if(n <= 1)
		return false;
	for(int i = 2; i * i <= n; i++)
	{
		if(n % i == 0)
			return false;
	}
	return true;
}

(2)获取素数表(获取 n 以内的所有素数)

a. 枚举法

时间复杂度

O(n * n1/2),适用于 n < 105

int num = 0; 
vector<int> v;

while (num <= n)
{
	if (isPrime(num))
		v.push_back(num);
	num++;
}
//打印
for (auto x : v)
	cout << x << ' ';

b. 埃式筛法

时间复杂度

O(n * loglogn),适用于 n > 105

vector<int> v[2];
//v[1]用来标记素数,0表示是素数,先初始化v[1]为全零
for (int i = 0; i <= n; i++) 
	v[1].push_back(0);

for (int i = 2; i <= n; i++)
{
	if (v[1][i] == 0)
	{
		v[0].push_back(i);
		for (int j = i + i; j <= n; j += i)
		{
			v[1][j] = 1;
		}
	}
}

5. 大整数运算(非负数的情况)

(1)《算法笔记》中结构体写法

(2) 直接写法(代码简洁一点)

a. 高精度与低精度的除法

思路
  1. 某位的商 = (上一步余数 * 10 + 该位值) / 除数;
    余数 = (上一步余数 * 10 + 该位值) % 除数;
  2. 特判:首位只有商不为零或者len == 1的情况下才有资格输出
//20220221 16:11 - 16:24
#include <iostream>

using namespace std;

int main()
{
	string s;
	int b, r = 0, quo = 0; //quotient:商
	cin >> s >> b;
	int len = s.length();

	//第一位特判,quo = 0是不能输出零
	quo = (s[0] - '0') / b;
	if (quo != 0 || len == 1)
		cout << quo;
	r = (s[0] - '0') % b;

	//quo = (r * 10 + x - '0') / b; r =  (r * 10 + x - '0') % b;
	for (int i = 1; i < len; i++)
	{
		quo = (r * 10 + s[i] - '0') / b;
		cout << quo;
		r = (r * 10 + s[i] - '0') % b;
	}

	cout << ' ' << r << endl;

	return 0;
}

b. 高精度与低精度的乘法

思路
  1. 某位的结果 = (上一步的进位 + 该位 * 低精度乘数) % 10
    进位 = (上一步的进位 + 该位 * 低精度乘数) / 10
  2. 特判:①低精度乘数为零时
    ②最后一位的进位(有可能不止一位),直接push_back到vector里等待输出
#include <iostream>
#include <vector>

using namespace std;

int main()
{
	string s;
	int b;
	cin >> s >> b;
	int len = s.length(), pro, carry = 0; // product记录当前位的乘积, carry记录进位
	vector<int> res;
	 
	//特判b == 0时的情况
	if (b == 0)
	{
		cout << 0 << endl;
		return 0;
	}
	
	for (int i = len - 1; i >= 0; i--)
	{
		pro = ((s[i] - '0') * b + carry) % 10;
		res.push_back(pro);
		carry = ((s[i] - '0') * b + carry) / 10;
	}

	//处理最后一位的进位
	if (carry != 0)
		res.push_back(carry);
		
 	//输出
	for (int i = res.size() - 1; i >= 0; i--)
	{
		cout << res[i];
	}

	return 0;
}

c. 高精度加法

思路
  1. 对齐a, b(swap()和补零)
  2. 某位的结果 = (上一步的进位 + 本位的和) % 10
    进位 = (上一步的进位 + 本位的和) / 10
  3. 最后一位的进位不为零则push_back(carry)进结果
	string a, b;
	cin >> a >> b;
	//1.a长b短,对齐ab
	if (a.length() < b.length()) 
		swap(a, b);
	int temp = a.length() - b.length();
	for (int i = 0; i < temp; i++)
	{
		b = '0' + b;
	}

	int n = a.length(), sum, carry = 0;
	vector<int> v;
	for (int i = n - 1; i >= 0; i--)
	{
		sum = ((a[i] - '0') + (b[i] - '0') + carry) % 10;
		v.push_back(sum);
		carry = ((a[i] - '0') + (b[i] - '0') + carry) / 10;
	}
	if (carry != 0)
		v.push_back(carry);

	for (int i = v.size() - 1; i >= 0; i--)
		cout << v[i];

	cout << endl;

 }

c. 高精度减法

思路
  1. 保证a > b
  2. 某位的结果 =
    ①够减;本位差
    ②不够减;高位–,本位被减数 + 10 - 减数
  3. 去除高位多余的零,但要保证结果至少有一位数
	string a, b;
	cin >> a >> b;
	//保证a > b
	if (a.length() < b.length() ||(a.length() == b.length() && a < b)) 
		swap(a, b);
	int temp = a.length() - b.length();
	
	for (int i = 0; i < temp; i++)
	{
		b = '0' + b;
	}
	
	
	int n = a.length(), diff;
	vector<int> v;
	for (int i = n - 1; i >= 0; i--)
	{
	if (a[i] < b[i])
	{
		a[i - 1]--;
		diff = (a[i] - '0') + 10 - (b[i] - '0');
		v.push_back(diff);
	}
	else
		v.push_back((a[i] - '0') - (b[i] - '0'));
	}
	while (v[v.size() - 1] == 0 && v.size() != 1)
		v.pop_back();
	
	for (int i = v.size() - 1; i >= 0; i--)
		cout << v[i];
	
		cout << endl;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值