复盘笔记-算法基础课2

高精度

存储:大整数在C++里用数组存储,存储时应当高位在前,即在数组第一位存个位,因为若出现进位,则需要在数组末尾增添的情况比较方便。

有四种情况,A+B,A-B,A*a,A/a。现分别讨论:

情况一:A+B,整数规模在10^6之内。算法思想就是模拟人工计算过程。即各个位数相加后进行结果处理,若出现进位则在下一位的处理加上进位进行

#include<bits/stdc++.h>
using namespace std;

vector<int> add(vector<int> &a,vector<int> &b){
	int t=0;              //用来保存进位
	vector<int >c;
	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> va,vb;
	cin>>a>>b;
	for(int i=a.length()-1;i>=0;i--) va.push_back(a[i]-'0');  //将加数的个位移动到第一位
	for(int i=b.length()-1;i>=0;i--) vb.push_back(b[i]-'0');
	auto vc=add(va,vb);//auto变量编译器自己判定变量类型
	for(int i=vc.size()-1;i>=0;i--) printf("%d",vc[i]);//输出时从高位到地位进行输出
}

情况二:A-B,整数规模在10^6之内。

每次相对应的数位进行相减,若够减,直接减,若不够减,则需借位。计算相对应的数位是记得减去相对应的数位。在进行减法之前,可以先判断谁大,用大的数减去小的数之后,根据实际输入判断是否出现加负号的情况。

#include<bits/stdc++.h>
using namespace std;

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> sub(vector<int>& A,vector<int> &B){
	int t=0;              //用来保存借位
	vector<int>c;
	for(int i=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;
	}
    while(c.size()>1&&c.back()==0) c.pop_back();//若出现相减之后只有高位出现0的情况,将后面的 
                                                // 0弹出
	return c;
}
int main(){
	string a,b;
	vector<int> A,B;
	cin>>a>>b;
	for(int i=a.length()-1;i>=0;i--) A.push_back(a[i]-'0');  //将需要处理的数据的个位移动到 
                                                             //第一位
	for(int i=b.length()-1;i>=0;i--) B.push_back(b[i]-'0');   
    if(cmp(A,B)){
        auto c=sub(A,B);
        for(int i=c.size()-1;i>=0;i--) printf("%d",c[i]);//输出时从高位到地位进行输出
    }
    return 0;
}

情况三:A*a,A的长度<10^6,a<10^5

高精度整数*低精度整数。手算模拟情况可以得知,每次是将A的大数分别乘上a,当前数位保留的是乘法结果对10取余,进位的大小则是除以10的结果。

#include<bits/stdc++.h>
using namespace std;

vector<int> mul(vector<int>&A,int &b){
	int t=0;              //用来保存进位
	vector<int> c;
	for(int i=0;i<A.size() || t;i++){//在A没循环完或者进位t还没处理完。
		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(int i=a.length()-1;i>=0;i--) A.push_back(a[i]-'0');  //将数的个位移动到第一位

	auto c=mul(A,b);//auto变量编译器自己判定变量类型
	for(int i=c.size()-1;i>=0;i--) printf("%d",c[i]);//输出时从高位到地位进行输出
}

情况四:A/a

高精度整数/低精度整数。

#include<bits/stdc++.h>
using namespace std;

vector<int> mul(vector<int>&A,int &b,int &r){
	r=0;              //用来保存余数
	vector<int> c;
	for(int i=A.size()-1;i>=0;i--){//在A没循环完或者进位t还没处理完。
		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();//若出现相减之后只有高位出现0的情况,将后面的 
                                                // 0弹出
	return c;
}
int main(){
	string a;
    int b;
	vector<int> A;
	cin>>a>>b;
	for(int i=a.length()-1;i>=0;i--) A.push_back(a[i]-'0');  //将数的个位移动到第一位

	auto c=div(A,b,r);//auto变量编译器自己判定变量类型
	for(int i=c.size()-1;i>=0;i--) printf("%d",c[i]);//输出时从高位到地位进行输出
    cout<<endl<<r<<endl;
return 0;
}

前缀和

前缀和Si=a1+a2+a3+...an

如何求Si以及作用。Si是通过前i-1项的前缀和加上第i个值。作用是能够快速的求出任意一段区间Sr-Sl-1的和。注意下标从1开始,可以省去边界判断的问题。

一维前缀和

#include<bits/stdc++.h>
using namespace std;
const int N=100010;

int n,m;
int a[N],s[N];
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++) 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]);
	}
}

二维前缀和

Sij表示左上角部分的前缀和

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
int n,m,q;
ll a[N][N],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("%lld",&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;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		printf("%lld\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
	}
}

差分

已知一个数组a,构造出数组b,使得ai=b1+b2+b3+...bi

一维差分

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],b[N];//A是原数组,B是差分数组
int main(){
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1];
	while(m--){
		int l,r,c;
		scanf("%d%d%d",&l,&r,&c);
		b[l]+=c;
		b[r+1]-=c;
	}
	for(int i=1;i<=n;i++){
		a[i]=a[i-1]+b[i];
		printf("%d ",a[i]);
	}
}

差分矩阵(二维差分)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010;
ll a[N][N],b[N][N];
void Insert(int x1,int y1,int x2,int y2,ll c){
	b[x1][y1]+=c;
	b[x2+1][y1]-=c;
	b[x1][y2+1]-=c;
	b[x2+1][y2+1]+=c;
}
int main(){
	int n,m,q;
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			scanf("%lld",&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;
		ll c; 
		scanf("%d%d%d%d%lld",&x1,&y1,&x2,&y2,&c);
		Insert(x1,y1,x2,y2,c);//起初将a数组看成空的,执行插入操作
	}
	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("%lld ",b[i][j]);
		cout<<endl;
	}
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值