作者:@黑眼圈ing
机电专业 工科男
CSDN@黑眼圈ing
每天进步一点点!
备战蓝桥杯的第三天,今天学习的是常考的高精度算法和前缀和差分,我的学习方法:1.首先了解算法的主要思想,不能单单记住模板,2.多背模板,能够做到快速的写出模板,调debug,3.坚持每天做2、3道算法题,`
先赞后看,养成习惯! ! ! ^ _ ^ ❤️ ❤️ ❤️
*码字不易,大家的支持是我坚持的动力。点赞后不要忘了关注我哦!
一、高精度加法
高精度加法模板 //C = A + B
vector<int> add(vector<int>& A,vector<int>& B)
{ if(A.size()<B.size()) return add(B,A);
vector<int> C;
for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
{ t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;//进位
}
if(t) C.push_back(1);//处理最高位进位
}
高精度加法思想仿照我们小学的计算方法,从低位到高位依次相加,超过10就像前进一位,首先第一位t=0,C1=(A1+B1+t)%10的余数,进位t=(A1+B1+t)/10,第二位C2=(A2+B2+t)%10的余数,进位t=(A2+B2+t)/10,接下来重复找到规律
高精度加法完整版
#include <iostream>
#include <vector>
using namespace std;
// C =A + B
vector<int> add(vector<int>& A,vector<int>& B)
{ if(A.size()<B.size()) return add(B,A);
vector<int> C;
for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
{ t+=A[i];
if(i<B.size()) t+=B[i];
C.push_back(t%10);
t/=10;//进位
}
if(t) C.push_back(1);//处理最高位进位
return C;
}
int main()
{ string a,b;//数值太大要用字符串存储
vector<int> A,B;
cin>>a>>b;//123456
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//6 5 4 3 2 1
for(int i=b.size()-1;i>=0;i--) B.pusn_back(b[i]-'0');
auto C=add(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
return 0;
}
问题1:为什么有for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0')这步操作呢?
因为如果我们直接将字符串存入数组里,那么数组里a[0],a[1]等开始的位置存放的是高位,如果最高位要进1的话,就要将数据往后挪一位,时间复杂度就是O(n)。
将字符串逆序存入数组中,那么数组就是从低位到高位,最高位进位直接push_back
二、高精度减法
vector<int> sub(vector<int>& A,vector<int>& B)
{ vector<int> C;
int t=0;
for(int i=0;i<A.size();i++)
{ t=A[i]-t;
if(i<B.size()) t-=B[i];
C.push_back(t%10);
t/=10;
}
while(C.size()>1&&C.back()==0) C.pop_back();//处理前导0 "00034"的情况
return C;
}
//高精度减法完整版
#include <iostream>
#include <vector>
using namespace std;
// C =A - B
vector<int> sub(vector<int>& A,vector<int>& B)
{
vector<int> C;
for(int i=0,t=0;i<=A.size();i++)//从低位到高位相加
{ t=A[i]-t;
if(i<B.size()) t-=B[i];
C.push_back(t%10);
t/=10;//进位
}
while(C.size()>1&&C.back()==0) C.pop_back();//处理前导零
return C;
}
int main()
{ string a,b;//数值太大要用字符串存储
vector<int> A,B;
cin>>a>>b;//123456
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//6 5 4 3 2 1
for(int i=b.size()-1;i>=0;i--) B.pusn_back(b[i]-'0');
auto C=sub(A,B);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
return 0;
}
三、高精度乘法
//高精度乘法模板 高精度乘低精度
// C = A * b
vector<int> mul(vector<int>& A,int b)
{ vector<int> C;
int t=0;
for(int i=0;i<A.size()||t;i++)
{
if(i<A.size()) t+=A[i]*b;
C.push_back(t%10);
t/=10;
}
while(C.size()>1&&C.back()==0) C.pop_back();
return C:
}
//高精度乘法完整版
#include <iostream>
#include <vector>
using namespace std;
// C =A * b
vector<int> sub(vector<int>& A,int b)
{
vector<int> C;
int t=0;
for(int i=0;i<=A.size()||t;i++)//从低位到高位相加
{
if(i<B.size()) t+=A[i]*b;
C.push_back(t%10);
t/=10;//进位
}
while(C.size()>1&&C.back()==0) C.pop_back();//处理前导零
return C;
}
int main()
{ string a;//数值太大要用字符串存储
int b;
vector<int> A;
cin>>a>>b;//123456
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//6 5 4 3 2 1
auto C=mul(A,b);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
return 0;
}
四、高精度除法
//高精度除法模板
// C =A / b
vector<int> div(vector<int>& A,int b,int& r)
{ vector<int> C;
r=0;
for(int i=A.size()-1;i>=0;i--)
{ r=10*r+A[i];
C.push_back(r/b);
r %= b;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0) C.pop_back();
return C;
}
高精度除法需要注意的是除法从高位开始的,因为高精度加、减和乘都是从高位到低位输出的,所以高精度除法,我们要将C数组reverse !
//高精度除法完整版
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// C =A / b
vector<int> div(vector<int>& A,int b,int& r)
{
vector<int> C;
r=0;
for(int i=A.size()-1;i>=0;i--)//从高位到低位
{
r=10*r+A[i];
C.push_back(r/b);
r %= b;//进位
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0) C.pop_back();//处理前导零
return C;
}
int main()
{ string a;//数值太大要用字符串存储
int b;
vector<int> A;
cin>>a>>b;//123456
for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//6 5 4 3 2 1
int r;//余数
auto C=div(A,b,r);
for(int i=C.size()-1;i>=0;i--) cout<<C[i];//从高位到低位输出
return 0;
}
五、前缀和
1.一维前缀和
一维前缀和多用来解决数组一部分区间的和,例如:有一个数组a[10],求从a[3]~a[6]区间的和,如果用暴力求解的话,时间复杂度是O(n),所以一般我们采用前缀和来求解
一维前缀和我们需要知道的是:1.前缀和数组b[i]=a[1]+a[2]+a[3]+...+a[i] 2.求部分和l~r a[l]+...a[r]=b[r]-b[l-1]
#include <iostream>
using namespace std;
const int N=1e6;
int q[N],a[N];
int n,m;
int main()
{ scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&q[i]);
//前缀和数组
for(int i=1;i<=n;i++) a[i]=a[i-1]+q[i];
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",a[r]-a[l-1]);
}
return 0;
}
2.二位前缀和
二位前缀和用来求矩阵的子矩阵子和
二位前缀和我们要知道:s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j]
#include <iostream>
using namespace std;
const int N = 1010;
int n, m, q;
int s[N][N];
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
scanf("%d", &s[i][j]);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];
while (q -- )
{
int x1, y1, x2, y2;
scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
}
return 0;
}
六、差分
1.一维差分
一维差分:l~r区间加上c
一维差分我们要知道:差分数组q[l] += c,q[r+1] -= c
//构造差分数组
void insert(int l,int r,int c)
{
q[l] += c;
q[r+1] -= c;
}
insert(l,r,c);
大家可能会有疑问为什么插入可以构造差分数组呢?
我们假设`原数组: a1, a2, a3, …, an
构造数组:b1, b2, b3, …, bn 使得 ai = b1 + b2 + b3 + … + bi(a数组就称为b数组的前缀和数组)
一种构造方式
- b1 = a1, ----> a1 = b1
- b2 = a2 - a1, ----> a2 = b1 + b2
- b3 = a3 - a2, ----> a3 = b1 + b2 + b3
- … …
- … …
- b(n) = a(n) - a(n-1) ----> an = b1 + b2 + b3 + … + bn
- 原数组是a1, a2, a3, …, an, 可假设a数组初始时全是0, 立即推 ==> 差分数组b也全是0, 而给的a数组不是0,我们可以看作是进行了n次插入(insert)操作。
- a1, insert(1,1,a[1])
- a2, insert(2,2,a[2])
- a3, insert(3,3,a[3])
- …,
- an, insert(n,n,a[n])
void insert(int l, int r, int c) {
b[l] += c;
b[r + 1] -= c;
}
for (int i = 1; i <= n; i++) insert(i, i, a[i]);
假设 a[] = {1, 3, 2, 1, 4}
我们把 a 数组带入代码中走一遍,看看结果(注意 b数组是全局变量,所以默认值全为0)
b1 = b1 + a1 = 0 + 1 = 1***
b2 = b2 - a1 = 0 - 1 = -1
b2 = b2 + a2 = -1 + 3 = 2***
b3 = b3 - a2 = 0 - 3 = -3
b3 = b3 + a3 = -3 + 2 = -1***
b4 = b4 - a3 = 0 - 2 = -2
b4 = b4 + a4 = -2 + 1 = -1***
b5 = b5 - a4 = 0 - 1 = -1
b5 = b5 + a5 = -1 + 4 = 3***
b6 = b6 - a5 = 0 - 4 = -4***
请注意带***的就是构造出来的b差分数组!!!可以将b数组求前缀和 它就是a 数**组了
`
#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int a[N], b[N];
void insert(int l, int r, int c) {
b[l] += c;
b[r + 1] -= c;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) insert(i, i, a[i]);
while (m--) {
int l, r, c;
scanf("%d%d%d", &l, &r, &c);
insert(l, r, c);
}
for (int i = 1; i <= n; i++) b[i] += b[i - 1];
for (int i = 1; i <= n; i++) printf("%d ", b[i]);
return 0;
}
2.二位差分
#include <iostream>
using namespace std;
const int N=1010;
int q[N][N],b[N][N];
int n,m,k;
void insert(int x1,int y1,int x2,int y2,int c)
{
b[x2+1][y1]-=c;
b[x1][y1]+=c;
b[x1][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{ scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
{
for(int j=1;i<=m;i++)
scanf("%d",&q[i][j]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;i<=m;i++)
insert(i,j,i,j,q[i][j]);
}
while(k--)
{ int x1,y1,x2,y2,c;
cin>>x1>>x2>>y1>>y2>>c;
insert(x1,y1,x2,y2,c);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
printf("%d",b[i][j]);
}
printf("\n");
}
return 0;
}
总结
欢迎各位大佬评论,如果有哪里有错误的地方,还请各位大佬指出,我会虚心接受的!!!
每天进步一点儿!!!
感谢大佬们的支持!!!感谢大佬们的支持!!!感谢大佬们的支持!!!