高精度(C++)问题:
一共考4种问题:
- 大整数相加 A+B A,B<=10的6次方
- 大整数相减 A,B<=10的6次方
- 大整数相乘 A<=10的6次方, a<=10的9次方
- 大整数相除 A<=10的6次方, a<=10的9次方
// java有大整数类,python是无限大,不需要。
大整数是很大的,用字符数组写入。
0 1 2 3 4 5 6 7 8
9 8 7 6 5 4 3 2 1
先写低位存储大整数。浮点数用太少,暂时不学了。。。
小学学的加法是先低位进位再得结果:
1 2 3
8 9
—.–.------
2 1 2
大于10进位,不大于10不进位。
高精度加法1:
code1:
#include<iostream>
using namespace std;
//const int N=1e6+10;
//C=A+B;
vector<int> add(vector<int> &A,vector<int> &B){
vector<int> C;
int t=0;
for(int i=0;i<A.size()||i<B.size();i++){
if(i<A.size()) 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;//a="123456"
for(i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
auto C=add(A,B);//auto是编译器可以自己推断变量类型
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
换一下写法,提前判断:
#include<iostream>
using namespace std;
//const int N=1e6+10;
//C=A+B;
vector<int> add(vector<int> &A,vector<int> &B){
vector<int> C;
if(A.size()<B.size()) return add(B,A);
int t=0;
for(int i=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;//a="123456"
for(i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
auto C=add(A,B);//auto是编译器可以自己推断变量类型
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
大数减法:
A3 A2 A1 A0
B2 B1 B0
判断Ai-Bi-t>=0 :Ai-Bi-=t
判断Ai-Bi-t<0 :Ai-Bi+10=t
分两种情况:
A>=B: A-B;
a<b : -(B-A);
code:
#include<iostream>
using namespace std;
//const int N=1e6+10;
//C=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();i>=0;i--)
if(A[i]!=B[i])
return A[i]>B[i];
return true;
}
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)%10);
if(t<0) t=1;
else t=0;
}
//前导0 指的是
/*
1 2 3
1 2 0
---------------
0 0 3//这里的0就是前导0
*/
while(C.size()>1&&c.back()==0) c.pop_back();//去掉前导0
return C;
}
int main(){
string a,b;
vector<int> A,B;
cin>>a>>b;//a="123456"
for(i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
for(i=b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
if(cmp(A,B)){
auto C=sub(A,B);//auto是编译器可以自己推断变量类型
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
}
return 0;
}
高精度乘法:
说明一个问题:
1 2 3
1 2
t3 t2 t1
C3 C2 C1
C0=(3x12)%10=6;
t1=(3*12)/10 =3;
C1=(2X12+t+%10=7
t2=2
C2=(1*12)%10=1
t3=1
C3=1
以上问题 说明了一个模运算和权重为10的变化。
code:
#include<iostream>
#include<vector>
using namespace std;
const int N=1e6+10;
//C=A+B;
vector<int> mul(vector<int> &A,int b){
vector<int> C;
int t=0;//第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;
}
return C;
}
int main(){
string a;
int b;
vector<int> A;
cin>>a>>b;
for(i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//a[i]-'0' 是字符数组转到真实的整数。 常见字符ASCLL码:A :97 ,a: 65,0:48
auto C=mul(A,b);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
return 0;
}
大数的除法
#include<iostream>
#include<vector>
using namespace std;
const int N=1e6+10;
//C=A+B;
vector<int> div(vector<int> &A,int b,int &r){
vector<int> C;
r=0;
for(int i=A.size();i>=0;i--){
r=r*10+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;
for(i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');//a[i]-'0' 是字符数组转到真实的整数。 常见字符ASCLL码:A :97 ,a: 65,0:48
auto C=div(A,b,r);
for(int i=C.size()-1;i>=0;i--) printf("%d",C[i]);
cout<<endl<<r<<endl;
return 0;
}
前缀和
算是一个递推式子
:Si=a1+a2+…+an, s0=0.
2.如何算 Sn. S[i]=S[i-1]+an.
3,.作用 : O(n)—> O(1)的复杂度变化
两个式子相减得出:递推式
code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,m;
int a[N],s[N];
int main(){
ios::sync_with_stdio(false);//提高cin的速度,但是不能再用scanf ,速度还是小于scanf
//scanf("%d%d",&n,&m);
cin>>n>>m;
for(int i=1;i<n;i++) cin>>a[i];
for(int i=1;i<n;i++) s[i]=s[i-1]+a[i];//前缀和得初始化
while(m--){
int l,r;
cin>>l>>r;
printf("%d\n",s[r]-s[l-1]);//区间和的计算
}
return 0;
}
子矩阵的和
容斥原理思考一下可好。
code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=10010;
int n,m,q;
int a[N][N],s[N][N];
int main(){
// ios::sync_with_stdio(false);//提高cin的速度,但是不能再用scanf ,速度还是小于scanf
scanf("%d%d",&n,&m,&q);
//cin>>n>>m>>q;
for(int i=1;i<n;i++)
for(int j=1;j<m;j++)
// cin>>a[i][j];
scanf("%d",&a[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]+a[i][j];//求前缀和
while(q--){
int x1,y1,x2,y2;
// cin>>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;
}
差分:
本质是: 前缀和的逆运算
a1,a2,…an
构造b1,b2,b3,…bn
a数组是b数组的差分,b数组是a数组的前缀和,类似积分与微分。
b1=a1;
b2=a2-a1;
b3=a3-a2;
bn=b1+b2+b3+…
【l,r】+C,a1+C,a2+C,a3+C,…an+C. 差分实现时间复杂度为O(1),给原数组每个元素加上固定的值。
l-------->r
b(l+c)------->a(l+c)
code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=100010;
int m,n;
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:00暂停