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. 高精度与低精度的除法
思路
- 某位的商 = (上一步余数 * 10 + 该位值) / 除数;
余数 = (上一步余数 * 10 + 该位值) % 除数; - 特判:首位只有商不为零或者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. 高精度与低精度的乘法
思路
- 某位的结果 = (上一步的进位 + 该位 * 低精度乘数) % 10
进位 = (上一步的进位 + 该位 * 低精度乘数) / 10 - 特判:①低精度乘数为零时
②最后一位的进位(有可能不止一位),直接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. 高精度加法
思路
- 对齐a, b(
swap()
和补零) - 某位的结果 = (上一步的进位 + 本位的和) % 10
进位 = (上一步的进位 + 本位的和) / 10 - 最后一位的进位不为零则
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. 高精度减法
思路
- 保证a > b
- 某位的结果 =
①够减;本位差
②不够减;高位–,本位被减数 + 10 - 减数 - 去除高位多余的零,但要保证结果至少有一位数
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;