基础算法板子库


这里的python全是python3的
详情请见acwing,acwing赛高
力求快、准、狠、短


基础算法

#快速排序

O(nlogn)

----c++版

https://www.acwing.com/problem/content/787/

#include<iostream>
using namespace std;
const int N = 1e6+10;
int a[N];

void qs(int q[], int l, int r)
{
    if(l >= r) return;
    int x = q[l+r >> 1], i = l-1, j = 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]);
    }
    qs(q, l, j);
    qs(q, j+1, r);
}
int main(){
    int n;scanf("%d", &n);
    for(int i=1;i<=n;i++)scanf("%d", &a[i]);
    qs(a, 1, n);
    for(int i=1;i<=n;i++)printf("%d ", a[i]);
    return 0;
}

----python版

def swap(t1,t2):
    return t2,t1
def qs(c,l,r):
    if(l>=r): return
    x=c[(l+r)//2]
    i=l-1
    j=r+1
    while(i<j):
        while True:
            i+=1
            if(c[i]>=x):
                break
        while True:
            j-=1
            if(c[j]<=x):
                break
        if(i<j):
            #c[i],c[j]=c[j],c[i]
            c[i],c[j]=swap(c[i],c[j])
    qs(c,l,j)
    qs(c,j+1,r)

c=[1,4,6,2,7,4,7,9,4,8]
qs(c,0,9)
print(c)
#c[0],c[9]=swap(c[0],c[9])
#print(c)


#归并排序

可以用来解决逆序对的问题
O(nlogn)

----c++版

https://www.acwing.com/problem/content/789/

#include<iostream>
using namespace std;
const int N=1e6+10;
int a[N],tmp[N];
void ms(int q[], int l, int r){
    if(l>=r)return;
    int mid = r+l>>1;
    ms(q, l, mid);ms(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=0,j=l;j<=r;i++,j++)q[j]=tmp[i];
}
int main(){
    int n;scanf("%d", &n);
    for(int i=1;i<=n;i++)scanf("%d", &a[i]);
    ms(a,1,n);
    for(int i=1;i<=n;i++)printf("%d ", a[i]);
    return 0;
}

----python版

a = [66,11,45,79,79,4,35,987,10,0,54,3]

tmp = len(a)*[0]
def ms(q, l, r):
    if l >= r:
        return
    mid = (l+r)//2
    ms(q, l, mid); ms(q, mid+1, r)
    k = 0
    i = l
    j = mid+1
    while (i <= mid) and (j <= r):
        if q[i] <= q[j]:
            tmp[k] = q[i]
            k += 1; i += 1
        else:
            tmp[k] = q[j]
            k += 1; j += 1
    while i <= mid:
        tmp[k] = q[i]
        k += 1; i += 1
    while j <= r:
        tmp[k] = q[j]
        k += 1; j += 1
    for i in range(l, r+1):
        q[i] = tmp[i-l]
# 下标
ms(a, 0, len(a)-1)
print(a)

----python版2

n = int(input())
list1 = list(map(int, input().split()))

def merge_sort(list1):
    if len(list1) <= 1:
        return
    mid = len(list1) // 2
    L = list1[:mid]
    R = list1[mid:]
    merge_sort(L)
    merge_sort(R)

    i = j = k = 0
    while i < len(L) and j < len(R):
        if L[i] <= R[j]:
            list1[k] = L[i]
            i += 1
        else:
            list1[k] = R[j]
            j += 1
        k += 1
    while i < len(L):
        list1[k] = L[i]
        k += 1
        i += 1
    while j < len(R):
        list1[k] = R[j]
        k += 1
        j += 1

if __name__ == "__main__":
    merge_sort(list1)
    for i in list1:
        print(i, end=" ")

作者:肆十一
链接:https://www.acwing.com/solution/content/4628/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#整数二分查找

O(log n)

----c++版

https://www.acwing.com/problem/content/791/
找右 +1 <= 提l -1

#include<iostream>
using namespace std;

const int N=1e6+10;
int n,m;
int a[N];
int main()
{
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    
    while(m--)
    {
        int x;scanf("%d",&x);
        int l=1,r=n;
        while(l<r){
            int mid=l+r>>1;
            if(a[mid]>=x)r=mid;
            else l=mid+1;
        }
        if(a[l]!=x)cout<<"-1 -1"<<endl;
        else{
            cout<<l-1<<" ";
            l=1,r=n;
            while(l<r){
                int mid=l+r+1>>1;
                if(a[mid]<=x)l=mid;
                else r=mid-1;
            }
            cout<<l-1<<endl;
        }
    }
    return 0;
}

----python版

q = [1,2,3,4,4,5,5,6,6,7,7,8,9]

def bs_left(c, l, r, x):
    while l < r:
        mid = (l+r)//2
        if c[mid-1] >= x:
            r = mid
        else:
            l = mid+1
    return l-1

def bs_right(c, l, r, x):
    while l < r:
        mid = (l+r+1)//2
        if c[mid-1] <= x:
            l = mid
        else:
            r = mid-1
    return l-1
# 这个没写没找到的情况
for i in range(4, 6):
    print(bs_left(q, 1, len(q), i), bs_right(q, 1, len(q), i))

----python版2

def bsearch_first(data, k):
    l, r = 0, len(data)-1
    while l < r:
        mid = l + r >> 1
        if data[mid] >= k:
                r = mid
        else:
            l = mid + 1
    if data[l] != k:  # 不存在,返回-1
        return -1
    return l

def bsearch_last(data, k):
    l, r = 0, len(data)-1
    while l < r:
        mid = l + r + 1 >> 1
        if data[mid] <= k:
            l = mid
        else:
            r = mid - 1
    if data[l] != k:  # 不存在,返回-1
        return -1
    return l

if __name__ == '__main__':
    n, m = map(int, input().split())
    data = list(map(int, input().split()))
    while m:
        x = int(input())
        first = bsearch_first(data, x)
        last = bsearch_last(data, x)
        print(first, last)
        m -= 1

作者:WakeUp
链接:https://www.acwing.com/solution/content/6226/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

#浮点数二分查找

https://www.acwing.com/problem/content/792/

----c++版

#include<iostream>
#include<cmath>
using namespace std;

int main()
{
    double x;cin>>x;
    double l=0,r=x;
    while(fabs(l-r)>1e-8){
        double mid=(l+r)/2;
        if(fabs(mid*mid*mid)>=fabs(x))r=mid;//可能是负数
        else l=mid;
    }
    printf("%.6f",l);
}

#c++高精度算法

----基于面向对象的实现

https://blog.csdn.net/lafea/article/details/108935683

----高精度加法

https://www.acwing.com/problem/content/793/

//用vector实现
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

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;
    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');
    auto c=add(A,B);
    for(int i=c.size()-1;i>=0;i--)printf("%d", c[i]);
    return 0;
}

----高精度减法

https://www.acwing.com/problem/content/794/

#include<iostream>
#include<cstring>
#include<vector>

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();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;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 cout<<'-',C=sub(B, A);
    for(int i=C.size()-1;i>=0;i--)printf("%d", C[i]);
    return 0;
}

----高精度乘法

大数乘小数
https://www.acwing.com/problem/content/795/

#include<iostream>
#include<algorithm>
#include<cstring>
#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()||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;vector<int>A;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    auto c=mul(A,b);
    for(int i=c.size()-1;i>=0;i--)printf("%d", c[i]);
    return 0;
}

大数乘大数(带负数)

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<vector>
using namespace std;

vector<int> multi(vector<int>& a, vector<int>& b) {
	vector<int> c(a.size()+b.size());
	int t = 0;
	for (int i = 0; i < a.size(); i++)
		for (int j = 0; j < b.size(); j++) {
			c[i + j] += a[i] * b[j];//乘法原理
		}
	for(int i=0;i<c.size();i++)
		if (c[i] > 9) {
			c[i + 1] += c[i] / 10;//进位
			c[i] %= 10;
		}
	while (c.size() > 1 && c.back() == 0)c.pop_back();//去零
	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');
	int s = 1;
	if (A.back() == '-'-'0')
		s *= -1, A.pop_back();
	if (B.back() == '-'-'0')
		s *= -1, B.pop_back();
	auto c = multi(A, B);
	if (s == -1&&(c.size()!=1&&c[0]!=0))cout << '-';
	for (int i = c.size() - 1; i >= 0; i--)cout << c[i];
	return 0;
}

----高精度除法

大数除以小数
https://www.acwing.com/problem/content/796/

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;

vector<int> del(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);
        r%=b;
    }
    reverse(c.begin(),c.end());
    while(c.size()>1&&c.back()==0)c.pop_back();
    return c;
}

int main(){
    vector<int>A;string a;int b,r;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    auto c=del(A, b, r);
    for(int i=c.size()-1;i>=0;i--)printf("%d",c[i]);
    cout<<endl<<r;
    return 0;
}

#前缀和与差分

多见于区间操作
差分数组–前缀和–》原数组
原数组《–差分–前缀和数组
差分是前缀和的逆运算

##前缀和

----c++版

https://www.acwing.com/problem/content/797/

#include<iostream>
using namespace std;
const int N=1e6+10;

int a[N],s[N];
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++)s[i]+=a[i]+s[i-1];
    while(m--){
        int l,r;scanf("%d%d",&l,&r);
        int t=s[r]-s[l-1];
        printf("%d\n",t);
    }
    return 0;
}

##子矩阵的和

----c++版

https://www.acwing.com/problem/content/798/

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
const int N=2e6+10;
int a[1010][1010],s[1010][1010];

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++){
            //注意也从1开始
            scanf("%d",&a[i][j]);
            s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
        }
    }
    while(q--){
        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]);
    }
    return 0;
}

##差分

----c++版

https://www.acwing.com/problem/content/799/

#include<iostream>
#include<algorithm>
using namespace std;

const int N=1e6+10;
int a[N],d[N];

void insert(int &l,int &r,int &c){
    d[l]+=c;
    d[r+1]-=c;
}//主要是插入操作

int main(){
    int n,m;scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);//注意也要从1开始
    d[0]=0;
    for(int i=1;i<=n;i++)d[i]=a[i]-a[i-1];
    while(m--){
        int l,r,c;scanf("%d%d%d",&l,&r,&c);
        insert(l,r,c);
    }
    for(int i=1;i<=n;i++)a[i]=a[i-1]+d[i],printf("%d ",a[i]);
    return 0;
}

##差分矩阵

----c++版

https://www.acwing.com/problem/content/800/
差分矩阵中的每一个单元表示前面所有抬升的阶数和,还要在抬升几阶才到原数组的值

#include<iostream>
using namespace std;
const int N=1e6+10;
int a[1010][1010],d[1010][1010];

void op(int &x1,int &y1,int &x2,int &y2,int &c){
    d[x1][y1]+=c;
    d[x2+1][y1]-=c;
    d[x1][y2+1]-=c;
    d[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++){
            int t;scanf("%d",&t);
            op(i,j,i,j,t);
        }
    while(q--){
        int x1,x2,y1,y2,c;
        scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&c);
        op(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1];//做一个前缀和即可得原数组
        }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            printf("%d ",d[i][j]);
        }printf("\n");
    }
    return 0;
}

#双指针算法

如尺取法等

##最长连续不重复子列

----c++版

https://www.acwing.com/problem/content/801/

#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+10;

int a[N],s[N];
int main(){
    int n;cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    int j=0,m=0;
    for(int i=1;i<=n;i++){
        s[a[i]]++;
        while(s[a[i]]>1){//只要大于一,就说明有重复了!
            s[a[j]]--;
            j++;
        }
        m=max(m,i-j+1);//每次j移到重复元素的后一位上
    }
    cout<<m;
    return 0;
}

##数组元素目标和

----c++版

https://www.acwing.com/problem/content/802/

#include<iostream>
using namespace std;
const int N=1e6+10;

int a[N],b[N];
int main(){
    int n,m,x;cin>>n>>m>>x;
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    for(int i=0;i<m;i++)scanf("%d",&b[i]);
    
    int i=0,j=m-1;
    while(i<n&&j>=0){
        if(a[i]+b[j]>x)
            j--;
        else if(a[i]+b[j]<x)
            i++;
        else{
            printf("%d %d",i,j);
            break;
        }
    }
    return 0;
}

#位运算算法

##二进制表示中1的个数

https://www.acwing.com/problem/content/803/

----c++版1
#include<iostream>
using namespace std;

int main(){
    int n;cin>>n;
    while(n--){
        int t,ans=0;scanf("%d",&t);
        while(t){
            if(t&1){
                ans++;
            }
            t>>=1;
        }printf("%d ",ans);
    }
    return 0;
}
----c++版2(含lowbit操作)
#include<iostream>
using namespace std;
int lowbit(int q){//返回二进制表示的最右边的1所表示的数
    return q&(-q);
}

int main(){
    int n;cin>>n;
    while(n--){
        int q,ans=0;scanf("%d",&q);
        while(q)q-=lowbit(q),ans++;
        printf("%d ",ans);
    }
    return 0;
}

#离散化

##求区间和

----c++版

https://www.acwing.com/problem/content/804/

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

typedef pair<int,int> pll;
const int N=3e6+10;//一般开2-3倍
int n,m;
int a[N],s[N];
vector<int>alls;//所有被用到的坐标
vector<pll>add,query;//存所有要加的值和将要询问的区间

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;
}

int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        int x,c;cin>>x>>c;
        add.push_back({x,c});
        alls.push_back(x);
    }
    for(int i=0;i<m;i++){
        int l,r;cin>>l>>r;
        query.push_back({l,r});
        alls.push_back(l);alls.push_back(r);
    }
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());
    //unique去重,返回最后一位不重复的下一位,并把所有多余重复的元素排在这一位和这一位之后。然后再用erase将重复的真正清除掉
    for(auto item:add){
        int x=find(item.first);//映射
        a[x]+=item.second;
    }
    for(int i=1;i<=alls.size();i++)s[i]=s[i-1]+a[i];
    for(auto item:query){
        int l=find(item.first),r=find(item.second);
        cout<<s[r]-s[l-1]<<endl;
    }
    return 0;
}

#区间合并算法

----c++版

https://www.acwing.com/problem/content/805/

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

typedef pair<int,int> pll;
const int N=1e6+10;
int n;
vector<pll>segs;
void mergesegs(vector<pll>&segs){//核心操作
    vector<pll>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;//返回也行
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++){
        int l,r;cin>>l>>r;
        segs.push_back({l,r});
    }
    mergesegs(segs);
    cout<<segs.size()<<endl;
    return 0;
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法是一种用于解决单源最短路径问题的贪心算法。它的基本思想是从起点开始,每次寻找当前最短路径的节点并加入到已处理节点的集合中,然后更新与该节点相邻的节点的距离。这个过程不断重复,直到所有节点都被处理。 以下是Dijkstra算法C++实现: ```c++ #include <iostream> #include <vector> #include <queue> #include <limits.h> using namespace std; typedef pair<int, int> PII; // 存储节点编号和距离的pair const int N = 100010; // 图中最多有100010个节点 int n, m; // n表示节点数,m表示边数 int h[N], e[N], w[N], ne[N], idx; // 邻接表存储图 int dist[N]; // 存储起点到每个节点的最短距离 bool st[N]; // 存储每个节点是否已经加入已处理节点的集合中 void add(int a, int b, int c) // 添加一条边a->b,权重为c { e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; } int dijkstra(int start, int end) { memset(dist, 0x3f, sizeof dist); // 将起点到每个节点的距离初始化为无穷大 dist[start] = 0; // 起点到自己的距离为0 priority_queue<PII, vector<PII>, greater<PII>> q; // 小根堆存储节点编号和距离的pair q.push({0, start}); // 将起点加入小根堆中 while (q.size()) // 只要小根堆不为空,就继续搜索 { auto t = q.top(); q.pop(); int ver = t.second, distance = t.first; // 取出距离起点最近的节点及其距离 if (st[ver]) continue; // 如果该节点已经在已处理节点的集合中,就直接跳过 st[ver] = true; // 将该节点加入已处理节点的集合中 for (int i = h[ver]; i != -1; i = ne[i]) // 枚举该节点的所有出边 { int j = e[i]; // 取出这条边的终点编号 if (dist[j] > distance + w[i]) // 如果从起点到j的路径长度可以被更新 { dist[j] = distance + w[i]; // 更新路径长度 q.push({dist[j], j}); // 将更新后的节点加入小根堆中 } } } if (dist[end] == 0x3f3f3f3f) return -1; // 如果起点无法到达终点,就返回-1 return dist[end]; // 返回起点到终点的最短距离 } int main() { cin >> n >> m; memset(h, -1, sizeof h); while (m -- ) { int a, b, c; cin >> a >> b >> c; add(a, b, c); } int start, end; cin >> start >> end; cout << dijkstra(start, end) << endl; return 0; } ``` 在这个实现中,我们使用了邻接表来存储图,使用了小根堆来维护未处理节点集合中距离起点最近的节点。在实现过程中,我们需要注意以下几点: 1. 将起点到每个节点的距离初始化为无穷大(这里使用了0x3f3f3f3f); 2. 每次取出小根堆中距离起点最近的节点时,需要判断该节点是否已经在已处理节点的集合中; 3. 每次更新节点的距离时,需要将更新后的节点加入小根堆中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值