AcWing算法基础课——简单算法模板

 说明

        本篇文章只给出代码模板,以及自己对该模板的理解。如果想看正确的算法思路,可以移步AcWing官网看详情。链接:常用代码模板1——基础算法 - AcWing
        如有理解错误,欢迎大家批评指正。

简单算法模板

一、排序

1.1 快速排序模板

void quick_sort(int q[],int l,int r){//q是传入的数组,[l,r]是需要排序的范围
	if(l>=r) return;
	int i = l-1,j = r+1,x = q[l+r>>1];
	while(i<j){
		do i++;while(q[i]<x);
		do j--;while(q[j]>x);
		if(i<j) swap(q[i],q[j]);
	}
	quick_sort(q,l,j),quick(q,j+1,r);
}

1.2 归并排序

int tmp[N];//需要开一个临时数组tmp[],N至少跟传入的q[]数组的范围一致
void merge_sort(int q[],int l,int r){
	if(l>=r) return;
	int mid = r+l>>1;
	merge_sort(q,l,mid),merge_sort(q,mid+1,r);
    int k = 0,i = l,j = mid+1;
	while(i<=mid&&j<=r){
        if(q[i]<=q[j]) tmp[k++]=q[i++];
        else tmp[k++]=q[j++];
    }
    while(i<=mid) tmp[k++]=q[i++];
    while(j<=r) tmp[k++]=q[j++];
    for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}

二、 二分

2.1 整数二分模板1

bool check(int x){//check函数检查x是否满足某种性质
    //根据题意完善check()函数
}
//区间被划分为[l,mid],[mid+1,r]
int bsearch1(int l,int r){
    while(l<r){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid+1;
    }
    return l;
}

2.2 整数二分模板2

bool check(int x){//check函数检查x是否满足某种性质
    //根据题意完善check()函数
}
//区间被划分为[l,mid-1],[mid,r]
int bsearch2(int l,int r){
    while(l<r){
        int mid=l+r+1>>1;
        if(check(mid)) l=mid;
        else r=mid+1;
    }
    return l;
}

浮点数二分模板

bool check(double x){//check函数检查x是否满足某种性质
    //根据题意完善check()函数
}
double bsearch3(double l,double r){
    const double eps = 1e-8;//定义精度:如果题目要求精确到小数点后六位小数,eps最好开到1e-8保险
    while(r-l>eps){
        double mid = (l+r)/2;
        if(check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

三、高精度

3.1 高精度大数的存储

string a;cin >> a;//以字符串读入大整数
vector<int> A;//使用vector<int> A从个位往高位存储即逆序存储
for(int i = a.size()-1;i>=0;i--) A.push_back(s[i]-'0');

3.2 A+B模板

//C=A+B,A>=0,B>=0
//C++中&还可用于引用变量,常用于传参和定义别名
//如第6行代码,若写成
//vector<int> add(vector<int A,vector<int> B){
//可能会爆内存并且会严重拖慢程序运行速度
vector<int> add(vector<int> &A,vector<int> &B){//参数为大整数A,B,返回A+B的vector
    if(A.size()<B.size()) return add(B,A);
    vector<int> C;
    int t = 0;//t表示进位
    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);//如果最高位还有进位,则放入C中
    return C;
}

3.3 A-B模板

//C=A-B,A>=0,B>=0
vector<int> sub(vector<int> &A,vector<int> &B){//参数为大整数A,B,返回A-B的vector
    vector<int> C;
    int t = 0;//t表示借位
    for(int i = 0;i<C.size();i++){
        t=A[i]-t;
        if(i<B.size()) t-=B[i];
        C.push_back((t+10)%10);//(t+10)是为了防止t为负数导致取模后出现负数
        if(t<0) t=1;//有借位
        else t=0;//没有借位
    }
    while(C.size()>1&&C.back()==0) C.pop_back();//去除前导零
    return C;
}

3.4 A*b模板

//C=A*b,A>=0,b>=0
vector<int> mul(vector<int> &A,int b){//参数为大整数A,小整数b,返回A*b的vector
    vector<int> C;
    int t = 0;//t表示进位值(区别于A+B的进位,t可能是很大的)
    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;
}

3.5 A/b模板

//A/b==C...r,A>=0,b>=0
vector<int> div(vector<int> &A,int b,int &r){//参数为大整数A,小整数b,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());//反转C,方便去除前导零和输出
    while(C.size()>1&&C.back()==0) C.pop_back();//去除前导零
    return C;
}

四、前缀和、差分

4.1 一维前缀和模板

//如果我们要求数组a[]大量的子区间[l,r]的数之和
const int N = 1e6+5;
int a[N],prefix[N];//preifx[]为前缀和数组
void solve(){
	int n;cin >> n;//有n个数,1<=n<=1000000
	for(int i = 1;i<=n;i++) cin >> a[i];//下标一定从1开始
	//计算前缀和
	for(int i = 1;i<=n;i++) prefix[i]=prefix[i-1]+a[i];
	int q;cin >> q;//有q次询问
	while(q--){
		int l,r;cin >> l >> r;//[l,r]为询问区间
        //输出子区间[l,r]的数之和
		cout << prefix[r]-prefix[l-1] << endl;//endl -> '\n'
	}
}

4.2 二维前缀和模板

//如果我们要求矩阵a[][]大量的子矩阵(a[x1][y1]和a[x2][y2]组成的子矩阵)的数之和
const int N = 1e3+5;
int a[N][N],prefix[N][N];//prefix[][]为前缀和数组
void solve(){
    int n;cin >> n;//n行n列的矩阵,1<=n<=1000
    for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) cin >> a[i][j];//下标从1开始
    //计算前缀和
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++){
            prefix[i][j]=prefix[i-1][j]+prefix[i][j-1]+a[i][j]-preifx[i-1][j-1];
        }
    }
    int q;cin >> q;//q次询问
    while(q--){
        int x1,y1,x2,y2;cin >> x1 >> y1 >> x2 >> y2;//子矩阵的范围
        //输出子矩阵数之和
        cout << prefix[x2][y2]-preifx[x1-1][y2]-prefix[x2][y1-1]+prefix[x1-1][y1-1] << endl;//endl -> '\n'
    }
}

4.3 一维差分模板

//如果我们要大量统一修改数组a[]的区间[l,r]的值,使该区间的数都加上x,并要我们输出最终修改后的数组
const int N = 1e6+5;
int a[N],diff[N];//diff[]为差分数组
void solve(){
    int n;cin >> n;//有n个数,1<=n<=1000000
    for(int i = 1;i<=n;i++) cin >> a[i];//下标从1开始
    //计算差分数组
    for(int i = 1;i<=n;i++) diff[i]=a[i]-a[i-1];
    int q;cin >> q;//q次修改
    while(q--){
        int l,r,x;cin >> l >> r >> x;//[l,r]区间内的数都加x
        diff[l]+=x,diff[r+1]-=x;
    }
    for(int i = 1;i<=n;i++) a[i]=a[i-1]+diff[i];//构造修改后的数组a[]
    for(int i = 1;i<=n;i++) cout << a[i] << " \n"[i==n];//遍历输出,输出完最后一个数后输出换行
}

4.4 二维差分模板

//如果我们要大量统一修改矩阵a[][]的子矩阵(a[x1][y1]和a[x2][y2]形成的子矩阵),使该区间都加上x,并要我们输出最终修改后的数组
const int N = 1e3+5;
int a[N][N],diff[N][N];//diff[][]为差分数组
void update(int x1,int y1,int x2,int y2,int x){
    diff[x1][y1]+=x,diff[x2+1][y2+1]+=x,diff[x1][y2+1]-=x,diff[x2+1][y1]-=x;
}
void solve(){
    int n;cin >> n;//n行n列的矩阵,1<=n<=1000
    for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) cin >> a[i][j];//下标从1开始
    //计算差分数组
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++){
            update(i,j,i,j,a[i][j]);//计算其实就是每次修改一个1×1的小矩阵
        }
    }
    int q;cin >> q;//q次询问
    while(q--){
        int x1,y1,x2,y2;cin >> x1 >> y1 >> x2 >> y2;//子矩阵的范围
        //实现修改
        update(x1,y1,x2,y2,x);
    }
    //构造回最终修改后的矩阵a[][]
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++){
        	a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]-diff[i][j];
        }
    }
    //遍历输出,输出完一行后换行
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++) cout << a[i][j] << " \n"[j==n];
    }
}

五、双指针

5.1 双指针模板

bool check(int i,int j){
    //具体问题的逻辑
}
void solve(){
    for(int i = 0,j=0;i<n;i++){
        while(j<i&&check(i,j)) j++;
        //具体问题的逻辑
    }
}
//常见问题分类:
//    (1) 对于一个序列,用两个指针维护一段区间
//    (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
//上述分类大致都包含快慢指针和对撞指针等例题

六、位运算

情景1

        求整数n的第k位二进制数字

情景1模板

int find(int n){
    return n>>k&1;
}
void solve(){
    int n;cin >> n;//读入n
	cout << find(n) << endl;//调用函数并输出结果
}

情景2

        求整数n的二进制表示中最低位的1的值

情景2模板

int lowbit(int n){//这个计算就被称为lowbit操作
    return n&(-n);
}
//如:求6的lowbit,6表示二进制补码为(0……0110)即29个'0'+"110"
//-6的补码为(1……1010)即29个'1'+"010"
//而(0……0110)&(1……1010) --> (0……0010)即结果的二进制为30个'0'+"10",转换为十进制的值为2
void solve(){
    int n;cin >> n;//读入n
    cout << lowbit(n) << endl;//调用函数并输出结果
}

七、离散化

7.1 离散化模板

vector<int> alls;//存储所有待离散化的值
//使用vector排序去重(要求会默写)
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
// 二分求出x对应的离散化的值
int find(int x){//找到第一个大于等于x的位置
    int l=0,r=alls.size()-1;
    while (l<r){
        int mid=l+r>>1;
        if(alls[mid]>=x) r=mid;
        else l=mid+1;
    }
    return r+1;//映射到1,2,...,n
}

八、 区间合并

8.1 区间合并模板

// 将所有存在交集的区间合并
void merge(vector<PII> &segs){//PII->pair<int,int>,pair存储的是左端点l和右端点r的二元组,合并后的结果放回segs
    vector<PII> res;
    sort(segs.begin(),segs.end());
    int st=-2e9,ed=-2e9;
    for(auto seg:segs){
        if(ed<seg.first){
            if(st!=-2e9) res.push_back({st,ed});
            st=seg.first,ed=seg.second;
        }
        else ed=max(ed,seg.second);
    }
    if (st!=-2e9) res.push_back({st, ed});
    segs=res;
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Beau_Will

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值