Chapter1 基础算法(Ⅱ)

一、高精度(C++要学,JAVA和Python其实没必要学)

(1)一般有四种应用场景:

  • A+B (A、B是大整数,长度小于等于10^6)
  • A-B
  • A×a (a<=1000小整数)
  • A÷a

(2)大整数如何存储?

一般用数组一位位存储;一般先存数的个位,方便进位

(3)运算

  • 高精度加法 A+B

补充vector:

C++ vector的用法(整理)_wkq0825的专栏-优快云博客_c++ vector

#include <iostream>
#include <vector> 

using namespace std;

vector<int> add(vector<int> &A, vector<int> &B)//&? 没有也可以AC
{
	if (A.size()<B.size()) return add(B,A); //没有return会出错吧
	 
	vector<int> C;
	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(t); //是否进位
	return C; 
}
int main(){
	string a, b;
	vector<int> A, B; //定义int类型的容器 
	cin >> a >> b;
	for (int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0'); //在A的最后一个向量后插入一个元素
	for (int i = b.size()-1;i>=0;i--) B.push_back(b[i]-'0');
	
	auto C = add(A,B); //auto是指C的类型是由add的结果自动设定的
	
	for(int i = C.size()-1;i>=0;i--) cout<<C[i];
	cout << endl;
	
	return 0; 
}
  • 高精度减法 A-B 
#include <iostream>
#include <vector> 

using namespace std;

bool cmp(vector<int> &A, vector<int> &B) //a? 可以,无关 
{
	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> 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); //注意是+10再%10
		if(t < 0) t=1; //更新进位的值。如果是直接+t呢
		else t = 0; 
	}
	while(C.size()>1&&C.back()==0) C.pop_back(); //去掉结果的前置0(存在vector的后部)
	return C; 
}
int main()
{
	string a,b;
	vector<int> A,B;
	cin >> a >> b;
	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');
	
	vector<int> C;
	
	if(cmp(A,B)) C =sub(A,B);
	else C = sub(B,A),cout << '-';
	
	for(int i = C.size()-1;i>=0;i--) cout << C[i];
	cout << endl;
	
	return 0; 
}
  • 高精度乘法(大整数string×整数int)
#include <iostream>
#include <vector>

using namespace std;

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+t; //注意逻辑,是先乘起来再加 
		C.push_back(t%10);
		t /= 10;
	}
	if(t) C.push_back(t);
	
	while(C.size()>1&&C.back()==0) C.pop_back();
	return C;
}
int main(){
	string a;
	int b;
	vector<int> A,C;
	cin >> a >> b;
	for(int i =a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	
	C = mul(A,b);
	
	for(int i=C.size()-1;i>=0;i--) cout << C[i];
	cout << endl;
} 
  • 高精度除法
#include <iostream>
#include <vector>
#include <algorithm> 

using namespace std;

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 = r*10 + A[i];
		C.push_back(r/b); //是b不是10,与加减乘不同 
		r = 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,r;
	vector<int> A;
	cin >> a >> b;
	for(int i =a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	
	auto C = div(A,b,r);
	
	for(int i=C.size()-1;i>=0;i--) cout << C[i];
	cout << endl << r <<endl;
} 

二、前缀和与差分

1. 前缀和

有数组a1 a2 a3 ...an,则前缀和为s1 s2 s3 ...sn ,其中si=a1+a2+...+ai

为了统一操作,a0=0,s0=0

(1) 计算前缀和

si=si-1+ai

(2) 有何作用

计算数组在[ l , r ]之间的和,若使用常用的方法遍历,需O(n),而使用前缀和计算只需要sr-sl-1,即O(1)

#include <iostream>

using namespace std;

const int N = 100010; //const少了会出错 
int a[N], s[N];
int main()
{
	int m, n;
	cin >> n >> m;
	for(int i = 1;i <= n;i ++){
		scanf("%d",&a[i]);
	}
	for(int i = 1;i <=n;i ++){ //前缀和的初始化 
		s[i]=s[i-1]+a[i];
	}
 	while(m--){ //区间和的计算 
 		int l,r;
 		scanf("%d %d",&l,&r);
 		printf("%d\n",s[r]-s[l-1]);
    }
    return 0;
} 

补充:一般情况下scanf比cin快,可以在开头输入 " ios::sync_with_stdio(false); ” 使cin的速度与scanf类似,但此时不可使用scanf

2. 二维前缀和(子矩阵的和)

(1)二维前缀和

s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j]

 (2)作用

计算某子矩阵的和时,不用前缀和需要O(n^2),利用前缀和只需要O(1)

即s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1]

3. 差分(前缀和的逆运算)

(1)有a1 a2 a3 ... an,构造b1 b2 b3 ... bn使得a是b的前缀和,b是a数组的差分

(2)作用

一般情况下给原数组[l,r]之间每个数+c需要O(n),而若构造了差分数组只需要O(1),

即b[l]+c;b[r+1]-c

(3)如何构造差分数组

已知全为0的数组的差分数组也是全0。假设a数组刚开始全为0,其对应的差分数组b也全为0,要使a数组变成现在的a数组,可以看成是给a数组[i,i]区间加上ai,则只需全为0的差分数组b数组进行insert操作

#include <iostream>

using namespace std;

const int N = 100010; //const少了会出错 
int a[N],b[N];
void insert(int l,int r,int c){
	b[l] += c;
	b[r+1] -= c;
}
int main()
{
	int n,m;
	cin >> 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 = i;i<=n;i++){
		a[i]=a[i-1]+b[i];
	}
	for(int i = 1;i<=n;i++){
		cout << a[i] << ' ';
	}
    return 0;
} 

4. 二维差分

(1)二维数组an,构造一个二维数组bn使bn为an的差分数组,an为bn的前缀数组

(2)作用:对an的子矩阵中的数都加上c,若不使用差分数组需O(n^2),而借助差分数组只需O(1),即

b[x1][y1] +=c;
b[x1][y2+1] -= c;
b[x2+1][y1] -= c;
b[x2+1][y2+1] += c;

构造an的差分数组,方法与上文类似

#include <iostream>

using namespace std;

const int N = 1010; //const少了会出错 
int a[N][N],b[N][N];
void insert(int x1,int y1, int x2, int y2, int c){
	b[x1][y1] +=c;
	b[x1][y2+1] -= c;
	b[x2+1][y1] -= c;
	b[x2+1][y2+1] += c;
}
int main()
{
	int n,m,q;
	cin >> n >> m >> q;
	for(int i = 1;i<=n;i++){
		for(int j =1;j<=m;j++){
			scanf("%d",&a[i][j]);
		}
	}
	//构造差分数组
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			insert(i,j,i,j,a[i][j]);
		}
	} 
	while(q--){
		int x1,y1,x2,y2,c;
		scanf("%d %d %d %d %d",&x1,&y1,&x2,&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][j-1] + b[i-1][j] - b[i-1][j-1] +b[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			printf("%d ",b[i][j]);
		}
		printf("\n");
	}
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值