C++考虑高精度,Java有大整数类,Python默认数的范围是无穷大
高精度考察的类型:
将大整数的每个位存在数组里面去
存储:个位放在数组的第一个元素,原因是考虑到高位的进位,数据结构是数组时方便在末尾加上进位数。
计算公式: A i + B i + t = C i A_i+B_i+t=C_i Ai+Bi+t=Ci,其中 t t t代表进位,值为0或者1。
A2 A1 A0
* b
——————————————
C2 C1 C0
{ t 0 = 0 C 0 = ( 3 × b + t 0 ) % 10 , t 1 = ( 3 × b + t 0 ) / 10 . . . C i = ( A i × b + t i ) % 10 , t i = ( A i × b + t i ) / 10 \begin{cases}t_0=0\\ C_0 = (3 \times b +t_0) \%10, t_1 = (3\times b +t_0)/10\\ ...\\ C_i = (A_i \times b + t_i)\% 10,t_i = (A_i\times b+t_{i})/10\end{cases} ⎩ ⎨ ⎧t0=0C0=(3×b+t0)%10,t1=(3×b+t0)/10...Ci=(Ai×b+ti)%10,ti=(Ai×b+ti)/10
class Solution {
public:
string addStrings(string num1, string num2) {
vector<int> Num1, Num2, Num3;
// 字符串放入数组中:
for (int i = num1.size() - 1; i >= 0 ; --i) {
Num1.push_back(num1[i] - '0'); // 转换为数字放入数组中
}
for (int i = num2.size() - 1; i >= 0 ; --i) {
Num2.push_back(num2[i] - '0'); // 转换为数字放入数组中
}
// 从数组第一位往后加
int t = 0;
for (int i = 0; i < Num1.size() || i < Num2.size(); ++i) {
if (i < Num1.size()) t += Num1[i];
if (i < Num2.size()) t += Num2[i];
Num3.push_back(t % 10);
t /= 10;
}
if (t) Num3.push_back(1);
string num3;
for (int i = Num3.size() - 1; i >= 0; --i) {
num3 += Num3[i] + '0';
}
return num3;
}
};
步骤:
判断:若 A ≥ B A\geq B A≥B,那么正常计算,否则交换A和B,计算B-A
A i − B i − t = { A i − B i − t , A i − B i − t ≥ 0 A i − B i + 10 − t , A i − B i − t ≤ 0 A_i-B_i-t=\begin{cases}A_i-B_i-t,A_i-B_i-t\geq 0\\ A_i-B_i+10-t,A_i-B_i-t\leq 0\end{cases} Ai−Bi−t={Ai−Bi−t,Ai−Bi−t≥0Ai−Bi+10−t,Ai−Bi−t≤0
根据是否 A ≥ B A\geq B A≥B输出时考虑负号
// 高精度减法
// 判断是否有 A>=B
bool cmp(vector<int>& A, vector<int>& B) {
if (A.size() != B.size()) return A.size() > B.size();
// 否则长度不相等,从最高位开始判断
for (int i = A.size() - 1; i >= 0; --i) {
if (A[i] != B[i]) return A[i] > B[i];
}
return true;
}
vector<int> gaoJingDuMinus(vector<int>& A, vector<int>& B) {
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); ++i) { // 保证了A一定是大于B的
int temp = A[i] - t;
if (i < B.size()) temp -= B[i];
C.push_back((temp + 10) % 10); // 大于0和小于0的情况合起来写
if (temp < 0) t = 1;
else t = 0;
}
while(C.size() > 1 && C.back() == 0) C.pop_back(); // 避免出现003的情况
return C;
}
string sub(string& a, string& b) {
// 将字符串形式的数据存储在数组中
vector<int> A, B, C;
// 存储时数组的最低位存储数据的个位,以方便最高位的进位
for (int i = a.size() - 1; i >= 0; --i) {
A.push_back(a[i] - '0'); // 减去偏移量得到数值
}
for (int i = b.size() - 1; i >= 0 ; --i) {
B.push_back(b[i] - '0'); // 减去偏移量得到数值
}
// 首先要判断A B哪个大,返回的数组C是逆序的,即数组的第一位是个位
string c;
if (cmp(A, B)) C = gaoJingDuMinus(A, B);
else {
C = gaoJingDuMinus(B, A);
c.push_back('-'); // 结果应该是负数,添加一个符号
}
for (int i = C.size()-1; i >= 0; --i) {
c += C[i] + '0';
}
return c;
}
vector<int> mul(vector<int> &A, int b) {
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); ++i) {
t += (A[i] * b);
C.push_back(t % 10);
t = t / 10;
}
if (t) C.push_back(t % 10);
return C;
}
// 高精度除法 A/b,商是C,余数是r A若正存储的话比较方便,此处为了一致,仍然采取最低为
vector<int> div(vector<int>& A, int b, int& r) {
vector<int> C;
for (int i = A.size() - 1; i >= 0; ++i) {
C.push_back((r * 10 + A[i]) / b);
r = (r * 10 + A[i]) % b;
}
// 和A保持低位高位一样,所以reverse一下,其实不reverse的结果就是我们要输出的数据
reverse(C.begin(), C.end());
// 去除前导0
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
前缀和 S [ i ] S[i] S[i]:数组中的前i个数的和 a 1 + a 2 + . . . + a 3 a_1+a_2+...+a_3 a1+a2+...+a3
两个问题:
如何求 S [ i ] S[i] S[i]?:for循环一遍即可 S [ i ] = S [ i − 1 ] + a [ i ] S[i] = S[i-1] + a[i] S[i]=S[i−1]+a[i]
前缀和有什么作用:快速求出来原数组中一段数的和 [ l , r ] [l,r] [l,r]的数和—— S [ r ] − S [ l − 1 ] S[r]-S[l-1] S[r]−S[l−1]
即用一次计算计算出任意一段的和
主要是公式的推导,尝试题目:
剑指 Offer II 013. 二维子矩阵的和 题解 - 力扣(LeetCode)
304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
注意多减的要加回来。
#include
#include
using namespace std;
int main() {
int n, m;
vector<int> vec, S;
vec.push_back(0);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) {
int temp = 0;
scanf("%d", &temp);
vec.push_back(temp);
}
S.push_back(0);
for (int i = 1; i < vec.size(); ++i) {
int temp = S[i-1] + vec[i]; // 前i-1个数的和加上i
S.push_back(temp);
}
while(m--) {
int l, r;
scanf("%d%d", &l, &r);
printf("%d\n", S[r]-S[l-1]);
}
return 0;
}
差分是前缀和的逆过程。设有数组A,要求构造出一个数组B,使得数组A的元素 A [ i ] A[i] A[i]是 B [ 1 ] , B [ 2 ] , . . . , B [ i ] B[1],B[2],...,B[i] B[1],B[2],...,B[i]的前缀和。
b称为A的差分,A是b的前缀和
所以对A求一遍差分可以得到数组b,对B求一遍前缀和得到数组A
时间复杂度都是 O ( n ) O(n) O(n)
输入数组A[i]:A[1],A[2],...,A[i]
,构造其差分数组的步骤:
A[i]
等价于操作:
b[i] = b[i] + A[i];
b[i+1] = b[i+1] - A[i]
b[l] += c
b[c+1] -= c
#include
#include
using namespace std;
// 注意书写前缀和和差分的代码时,数组下标从1开始存
int main() {
int n, m;
scanf("%d%d", &n, &m);
vector<int> nums(n+1), res(n+2,0);
// 在输入的同时构造差分数组
for (int i = 1; i <= n; ++i) {
scanf("%d", &nums[i]);
res[i] += nums[i];
res[i+1] -= nums[i];
}
// m个变化操作
while (m--) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
res[l] += c;
res[r+1] -= c;
}
// 计算res数组的前缀和,得到最终的数组答案
for (int i = 1; i < res.size()-1; ++i) {
res[i] += res[i-1];
cout << res[i] << " ";
}
return 0;
}
习题练习 Day2
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vNcE4yo-1660036541264)(https://cdn.jsdelivr.net/gh/Holmes233666/blogImage@main/img/image-20220809162615854.png)]
对应力扣题目:
高精度
415. 字符串相加 - 力扣(LeetCode)
306. 累加数 - 力扣(LeetCode)
43. 字符串相乘 - 力扣(LeetCode)
前缀和
303. 区域和检索 - 数组不可变 - 力扣(LeetCode)
437. 路径总和 III - 力扣(LeetCode)
剑指 Offer II 013. 二维子矩阵的和 题解 - 力扣(LeetCode)
304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
差分
1109. 航班预订统计 - 力扣(LeetCode)
1526. 形成目标数组的子数组最少增加次数 - 力扣(LeetCode)