acwing基础算法

目录

第一章

Ⅰ.快速排序

Ⅱ.归并排序

Ⅲ.二分

有单调性的一定可以二分,但是二分不一定有单调性

整数二分

浮点数二分

Ⅳ.高精度

高精度加法:

高精度减法  

 高精度乘法  

高精度除法

Ⅴ.前缀和与差分

二维前缀和

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

一维差分

差分对时间的优化:

二维差分

Ⅵ.双指针算法

Ⅶ.位运算

Ⅷ.离散化

Ⅸ.区间合并

第五章

Ⅰ.01背包——只能选择0或1次

优化——降为一维

注意!!!

Ⅱ.完全背包——一个物品可以选择无数次

注意!!!

 优化1——出去k层

 优化2——同01背包


第一章

Ⅰ.快速排序

void quick_sort(int q[], int l, int r) {
    if (l >= r) return;

    int x = q[l], j = l;
    for (int i = l + 1; i <= r; i ++ ) 
        if (q[i] < x) swap(q[ ++ j], q[i]);

    swap(q[l], q[j]);
    quick_sort(q, l, j - 1);
    quick_sort(q, j + 1, r);
}

Ⅱ.归并排序

void merge_sort(int q[],int l,int r){

    if(l>=r) return ;

    int mid = l + r >> 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(i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}

归并排序的使用——逆序对的数量

Ⅲ.二分

有单调性的一定可以二分,但是二分不一定有单调性

整数二分

注意mid是否会越界,可以改为:
mid = l + (r-l)>>1

int bserach_1(int l,int r){
    while(l < r){
        int mid = l + r >>1;
        if(check(mid)) r = mid;//check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
int bsearch_2(int l,int r){
    while(l < r){
        int mid = l + r + 1 >> 1;
        if(check(mid)) l = mid;//check()判断mid是否满足性质
        else r = mid - 1;
    }
    return l;
}

浮点数二分

eps 表示精度,取决于题目对精度的要求, 一般比所求精度高 2

double bsearch(double l, double r) {
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求, 一般比所求精度高 2
    while (r - l > eps) {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

求三次方根——cbrt()

#include<cstdio>
#include<cmath>
int main(){
    double a;
    scanf("%lf",&a);
    printf("%.6lf",cbrt(a));
    return 0;
}

Ⅳ.高精度

高精度加法:

#include<bits/stdc++.h>
using namespace std;
vector<int>va,vb,vc;
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;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--) va.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) vb.push_back(b[i]-'0');
    vc=add(va,vb);
    for(int i=vc.size()-1;i>=0;i--) cout<<vc[i];
    return 0;
}

高精度减法  

#include<bits/stdc++.h>
using namespace std;
vector<int>va,vb,vc;
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){
    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;
    }
    while(c.size()>1&&c.back()==0) c.pop_back();//去掉前导零
    return c;
}
int main(){
    string a,b;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--) va.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--) vb.push_back(b[i]-'0');
    if(cmp(va,vb)) vc=sub(va,vb);
    else{
        vc=sub(vb,va);
        cout<<'-';
    }
    for(int i=vc.size()-1;i>=0;i--) cout<<vc[i];
    return 0;
}

 高精度乘法  

#include<bits/stdc++.h>
using namespace std;
vector<int>va,vb,vc;
vector<int> mul(vector<int> &a,int b){
    int t=0;
    vector<int> c;
    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;
}
int main(){
    string a;
    int b;
    cin>>a>>b;//高精度乘低精度
    for(int i=a.size()-1;i>=0;i--) va.push_back(a[i]-'0');
  //  for(int i=b.size()-1;i>=0;i--) vb.push_back(b[i]-'0');
    vc=mul(va,b);
    for(int i=vc.size()-1;i>=0;i--) cout<<vc[i];
    return 0;
}

高精度除法

#include<bits/stdc++.h>
using namespace std;
vector<int>va,vb,vc;
//商是c,余数是r
vector<int> div(vector<int> &a,int b,int &r){//注意引用余数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);
        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;
    cin>>a>>b;//高精度除以低精度
    for(int i=a.size()-1;i>=0;i--) va.push_back(a[i]-'0');
  //  for(int i=b.size()-1;i>=0;i--) vb.push_back(b[i]-'0');
    int r;
    vc=div(va,b,r);
    for(int i=vc.size()-1;i>=0;i--) cout<<vc[i];
    cout<<endl<<r;//余数
    return 0;
}

Ⅴ.前缀和与差分

二维前缀和

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

#include<bits/stdc++.h>
using namespace std;
const int N=1e3+10;
int n,m,p;
int a[N][N],s[N][N];
int main(){
    scanf("%d%d%d",&n,&m,&p);
    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++) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
    
    while(p--){
        int x1,x2,y1,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]);//注意加的是x1-1,y1-1
    }
    return 0;
}

一维差分

a[i]是b[i]的前缀和,b[i]是a[i]的差分

差分对时间的优化:

已知数组a,对a的[l,r]区间各项执行+c的操作

求出数组a的差分数组b,使b[l]+=c,b[r+1]-=c( 时间将为O(1) )

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n,m,c,l,r;
void insert(int l,int r,int c){//让a的[l,r]区间加上一个数c,可用b[l]+=c,b[r+1]-=c代替(O(n)的操作将为O(1))
    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]);//构造差分数组b[i]=a[i]-a[i-1],假定初始a数组各项为0,即差分数组b也为0,对a数组赋值就相当于对a数组插入一个数(让[1,1]区间加上a[1],让[2,2]区间加上a[2],...,让[n,n]区间加上a[n])
    
    while(m--){
        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;
}

二维差分

二维差分对时间优化:

已知区间左上角和右下角坐标

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

#include<bits/stdc++.h>
using namespace std;
const int N=1100;
int a[N][N],b[N][N];
int n,m,q;
void insert(int x1,int y1,int x2,int y2,int c){
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
int main(){
    scanf("%d%d%d",&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,x2,y1,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-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("%d ",b[i][j]);
        printf("\n");
    }
    return 0;
}

Ⅵ.双指针算法

Ⅶ.位运算

位运算

//O(nlogn)
#include<bits/stdc++.h>
using namespace std;
int lowbit(int x){//求x的最后一位1,如lowbit(1010)=10,lowbit(111100000)=100000
    return x&(-x);
}
int main(){
    int n;
    cin>>n;
    while(n--){
        int x;
        cin>>x;
        int res=0;
        while(x) x-=lowbit(x),res+=1;
        cout<<res<<' ';
    }
    return 0;
}

Ⅷ.离散化

离散化:  O((n+2∗m)log(n+2∗m)) 

 AcWing 802. 区间和分析过程

//离散化:  O((n+2∗m)log(n+2∗m))

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],s[N];
int n,m;
vector<int>alls;//存储所有待离散化的值
typedef pair<int,int>PII;
vector<PII>query,add;
vector<int>::iterator unique(vector<int> &a){//双指针算法
    int j=0;
    for(int i=0;i<a.size();i++){
        if(!i||a[i]!=a[i-1]) a[j++]=a[i];// a[0] ~ a[j - 1] 所有a中不重复的数

    }
    return a.begin()+j;//返回去重之后的尾地址
}

//二分求出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~n的下标上,方便前缀和的计算
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        int x,c;
        cin>>x>>c;
        alls.push_back(x);
        add.push_back({x,c});
    }
    for(int i=0;i<m;i++){
        int l,r;
        cin>>l>>r;
        alls.push_back(l);
        alls.push_back(r);
        query.push_back({l,r});
    }
    
    // 排序,去重
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls),alls.end());
    
    
    // 处理插入
    for(auto it:add){
        int x=find(it.first);
        a[x]+=it.second;
    }
    
    
    // 预处理前缀和
    for(int i=1;i<=alls.size();i++) s[i]=s[i-1]+a[i];
    

    // 处理询问
    for(auto it:query){
        int l=find(it.first),r=find(it.second);
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

Ⅸ.区间合并

#include<bits/stdc++.h>
using namespace std;
int n;
typedef pair<int,int>PII;
vector<PII>segs;

void merge(vector<PII> &a){//注意引用
    vector<PII> res;
    
    sort(a.begin(),a.end());//按区间左端点排序
    
    int st=-2e9,ed=-2e9;
    for(auto it:a){
        if(ed<it.first){//注意二者相等时属于是右端点右移的情况
            if(st!=-2e9) res.push_back({st,ed});
            st=it.first,ed=it.second;
        }
        else ed=max(ed,it.second);
    }
    if(st!=-2e9) res.push_back({st,ed});
    a=res;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int l,r;
        cin>>l>>r;
        segs.push_back({l,r});
    }
    
    merge(segs);//区间合并
    
    cout<<segs.size();
    return 0;
}

第五章

Ⅰ.01背包——只能选择0或1次

  • 01背包就是从前i个物品中选择总体积<=j的所有选择中的最大价值
  • f(i,j)由两部分组成:一是从1到第i-1个中找总体积不超过j的选法,二是从1到第i-1个中寻找总体积不超过j-v[i]的选法(一组数同时加上或减去某个数其大小排序不变)

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N],f[N][N];
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
	
	//f[0][0~m]=0,已初始化为0
	
	for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){//注意考虑m=0
            f[i][j]=f[i-1][j];
            if(j>=v[i]) f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i]);
        }
	}
	cout<<f[n][m];

	return 0;
}

优化——降为一维

原因:

  • f[i]只用到了f[i-1]层——将i消去(降维)
  • j一层只用到了j和j-v[i]——可以直接从j=v[i]开始循环

循环内:
第一行
f[i][j]=f[i-1][j]//优化前

f[j]=f[j]//优化后恒成立



第二行
if(j>=v[i])//恒成立



第三行
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])//优化前

f[j]=max(f[j],f[j-v[i]]+w[i])//优化后

注意!!!

若j层循环是递增循环,我们优化后得到的实际为

f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i])

而不是优化前的

f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i])

所以j的循环应是递减循环

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int n,m;
int v[N],w[N];
int f[N];
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];

    for(int i=1;i<=n;i++){
        for(int j=m;j>=v[i];j--){//注意循环条件
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m];
    return 0;
}

Ⅱ.完全背包——一个物品可以选择无数次

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int v[N],w[N],f[N][N];
int n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];

    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            for(int k=0;k*v[i]<=j;k++)
                f[i][j]=max(f[i][j],f[i-1][j-k*v[i]]+k*w[i]);//注意max内是f[i][j]
        }
    }
    cout<<f[n][m];
    return 0;
}

注意!!!

循环中max里的第一项必须是f[i][j],因为完全背包是从上一层的多个状态中取最大值,因为是多个状态,所以用循环维护f[i][j]为最大值,而01背包是上一层的两个状态中取最大值,所以可以用f[i-1][j]

 优化1——出去k层

f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w ,  f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)
f[i , j-v]= max(            f[i-1,j-v]   ,  f[i-1,j-2*v] + w , f[i-1,j-2*v]+2*w , .....)
由上两式,可得出如下递推关系: 
                        f[i][j]=max(f[i,j-v]+w , f[i-1][j])

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int v[N],w[N],f[N][N];
int n,m;
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];

    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            f[i][j]=f[i-1][j];
            if(j>=v[i]) f[i][j]=max(f[i][j],f[i][j-v[i]]+w[i]);
        }
    }
    cout<<f[n][m];
    return 0;
}

 优化后max第一项可以是f[i][j],因为完全背包已经优化为找两个状态——一是i取0,结果同上一个状态相同,二是i取非0

 优化2——同01背包

#include<iostream>
using namespace std;
const int N = 1010;
int f[N];
int v[N],w[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i = 1 ; i <= n ;i ++)
    {
        cin>>v[i]>>w[i];
    }

    for(int i = 1 ; i<=n ;i++)
    for(int j = v[i] ; j<=m ;j++)
    {
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    }
    cout<<f[m]<<endl;
}

注意同01背包的区别:

01背包j循环循环递减,原因是取f[i-1][j-v[i]]+w[i]

完全背包j循环循环递增,原因是取f[i][j-v[i]]+w[i]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值