高精度(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暫停