第一讲-------基础算法 ACwing

第一讲-------基础算法

快速排序----分治

确定分界点 q[l],q[(l+r)/2],q[r],随机
调整区间 (三部分)
<= x x >=x
递归处理左右两端

快速排序算法模板 
void quick_sort(int q[], int l, int 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_sort(q, j + 1, r);
}

归并排序----分治----双指针算法

1.确定分界点:以数组的中心点分l+r>>1;
2.递归排序左边和右边 使左右有序
3.归并:合二为一

归并排序算法模板

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

逆序对的数量(暴力,归并排序,树状数组)
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。。数据范围1≤n≤100000,数列中的元素的取值范围 [1,1e9]。
输入样例:
6
2 3 4 5 6 1
输出样例:
5


#include <iostream>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int a[N], tmp[N];
LL merge_sort(int q[], int l, int r)
{
    
    
    if (l >= r) return 0;
    int mid = l + r >> 1;
    LL res = 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
        {
    
    
            res += mid - i + 1;
            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];
    return res;
}
int main()
{
    
    
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    cout << merge_sort(a, 0, n - 1) << endl;
    return 0;
}

如果更新方式是r=mid,则mid=l+1>>1不用做任何处理,否则mid=l+r+1>>1;

整数二分算法模板

bool check(int x) {/* … */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:

int bsearch_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;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(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) {
    
    /* ... */} // 检查x是否满足某种性质

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

高精度加法

例题:给定两个正整数(不含前导 0),计算它们的和。数据范围1<=整数长度<=100000
输入样例:12 23 输出样例:35
代码:
不圧位代码

#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int> &A, vector<int> &B)
{
    
    
    if (A.size() < B.size()) return add(B, A);
    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;
    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 -- ) cout << C[i];
    cout << endl;
    return 0;
}9位的代码
#include <iostream>
#include <vector>
using namespace std;
const int base = 1000000000;
vector<int> add(vector<int> &A, vector<int> &B)
{
    
    
    if (A.size() < B.size()) return add(B, A);

    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 % base);
        t /= base;
    }
    if (t) C.push_back(t);
    return C;
}
int main()
{
    
    
    string a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- )
    {
    
    
        s += (a[i] - '0') * t;
        j ++, t *= 10;
        if (j == 9 || i == 0)
        {
    
    
            A.push_back(s);
            s = j = 0;
            t = 1;
        }
    }
    for (int i = b.size() - 1, s = 0, j = 0, t = 1; i >= 0; i -- )
    {
    
    
        s += (b[i] - '0') * t;
        j ++, t *= 10;
        if (j == 9 || i == 0)
        {
    
       B.push_back(s);
            s = j = 0;
            t = 1;
        }
    }
    auto C = add(A, B);
    cout << C.back();
    for (int i = C.size() - 2; i >= 0; i -- ) printf("%09d", C[i]);
    cout << endl;
    return 0;
}

高精度减法

给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。数据范围1≤整数长度≤1e5
输入样例:32 11 输出样例:21

#include <iostream>
#include <vector>
using namespace std;
vector<int> C;
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)
{
    
    
    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');
    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;
}

高精度乘法

 123*12=1476
把12看成整体,
(3*12)%10=6;
(2*12+(3*12)/10)%10=(2*12+3)%10=7;  
(1*12+(2*12+3)/10)%10=14%10=4;     
14/10=1;

例题:给定两个非负整数(不含前导 0) A 和 B,请你计算A×B 的值。1≤A的长度≤100000,0≤B≤10000
输入样例:2 3 输出样例:6

#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() || 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();    //*0的情况
    return C;
}
int main()
{
    
    
    string a;
    int b;
    cin >> a >> b;
    vector<int> A;
    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;
}

高精度除法

给定两个非负整数(不含前导 0) A,B,请你计算A/B 的商和余数,
数据范围1≤A的长度≤100000 ,1≤B≤10000,B 一定不为 0
输出格式:共两行,第一行输出所求的商,第二行输出所求余数。
输入样例:7 2
输出样例:3 1
代码:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
//r是余数.
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);
        r %= b;
    }
    reverse(C.begin(), C.end());
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}

int main()
{
    
    
    string a;
    vector<int> A;
    int B;
    cin >> a >> B;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    int r;
    auto C = div(A, B, r);
    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl << r << endl;
    return 0;
}
一维前缀和 
S[i] = a[1] + a[2] + ... a[i]
a[l] + ... + a[r] = S[r] - S[l - 1]
二维前缀和
S[i, j] = 第i行j列格子左上部分所有元素的和
以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为:
S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] + S[x1 - 1, y1 - 1]
一维差分
给区间[l, r]中的每个数加上c:B[l] += c, B[r + 1] -= c
二维差分 
给以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵中的所有元素加上c:
S[x1, y1] += c, S[x2 + 1, y1] -= c, S[x1, y2 + 1] -= c, S[x2 + 1, y2 + 1] += c

例题:输入一个 n 行 m列的整数矩阵,再输入 qq个操作,每个操作包含五个整数 x1,y1,x2,y2,c每个操作要将选中的子矩阵中的每个元素的值加上 c。请你将进行完所有操作后的矩阵输出。
输入格式:第一行包含整数 n,m,q。接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。
输出格式:表示所有操作进行完毕后的最终矩阵。
数据范围1≤n,m≤1000,1≤q≤100000,1≤x1≤x2≤n,1≤y1≤y2≤m,−1000≤c≤1000,−1000≤矩阵内元素的值≤1000
输入样例:
3 4 3
1 2 2 1
3 2 2 1
1 1 1 1
1 1 2 2 1
1 3 2 3 2
3 1 3 4 1
输出样例:
2 3 4 1
4 3 4 1
2 2 2 2

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int a[N][N], b[N][N];

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, y1, x2, y2, c;
        cin >> 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]);
        puts("");
    }

    return 0;
}

双指针算法 —— 模板题 AcWIng 799. 最长连续不重复子序列, AcWing 800. 数组元素的目标和

for (int i = 0, j = 0; i < n; i ++ )
{
    
    
    while (j < i && check(i, j)) j ++ ;
    // 具体问题的逻辑
}

常见问题分类:
(1) 对于一个序列,用两个指针维护一段区间
(2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作

例题:给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。数据范围1≤n≤1e5
输入样例:5
1 2 2 3 5
输出样例:3
代码:

#include <bits/stdc++.h>
using namespace std;
int const N=1e5+10;
int n;
int a[N],b[N];
int res,maxx;
int main()
{
    
    
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    for(int i=0,j=0;i<n;i++)
    {
    
    
        b[a[i]]++;
        while(j<i&&b[a[i]]>1)
            b[a[j++]]--;
        maxx=max(maxx,i-j+1);
    }
    cout<<maxx<<endl;
    return 0;
}

位运算

^异或:相同为0,不同为1 I或:有一个为1值为1
&与:同为1值为1 ~取反:将0变1,将1变0
<<左移:将一个数的各二进制位全部左移N位,右补0;
.>>右移:将一个数的各二进制位全部右移N位,移到右端的低位被舍弃,对于无符号数,高位补0

X=1010
原码 0…01010
反码 1…10101
补码 1…10110
~x+1==-x

n的二进制表示中第k位是几
1.先把第k位移到最后一位
2.看各位数是几
n>>k&1

 例如:输出-10的二进制  11......10110
      #include<iostream>
       using namespace std;
        int main()
           {
    
      int n=10;
              unsigned int x=-n;
              for(int i=31;i>=0;i--)
               cout<<(x>>i & 1)
               return 0;
           }

lowbit()函数 [树状数组的基本操作]
用来取一个二进制最低位的1与后面的0组成的数
例如 5(101) lowbit(5)=1 (1)
12(1100) lowbit(12)=4(100)
lowbit(x)
x&-x = x&(~x+1)
X=1010…100…0
~x=0101…011…1
~x+1=0101…100…0
x&(~x+1)=0000…100…0=100…0
应用:可以统计x里面的1的个数
例题
给定一个长度为 n 的数列,请你求出数列中每个数的二进制表示中 1 的个数。数据范围1≤n≤100000,
输入样例:5
1 2 3 4 5
输出样例:1 1 2 1 2

代码:#include<bits/stdc++.h>
using namespace std;
int lowbit(int x)
{
    
     return x&-x;}
int main()
{
    
       int n;cin>>n;
    while(n--)
    {
    
       int x;
        cin>>x;
        int res=0;
        while(x)
        {
    
      x-=lowbit(x);//每次减去x的最后一位
           res++;     }
        cout<<res<<" ";
    }
    return 0;  }
二进制     十进制  [秦九韶算法]
1010     10
1010  1*2+0=2  (10)             
1010  2*2+1=5  (101)            
1010  5*2+0=10  (1010)
int get(string s,int b)    //将b进制的数转化为十进制
{
    
    
   int res=0;
for(auto c:s)
  res+=res*b+(c-0);
  return res;
}
 

离散化

满足离散化的性质:值域大,数稀疏
a[ ] : { 1 , 3 , 100 , 200 , 500000000 } 映射
下标:0 1 2 3 4
1.a[]中可能有重复的元素 去重
2.如何计算出离散化的值 二分(此题有序)
例题:区间和
假定有一个无限长的数轴,数轴上每个坐标上的数都是 0。现在,我们首先进行 n 次操作,每次操作将某一位置 x上的数加 c。接下来,进行 m 次询问,每个询问包含两个整数 l 和 r,你需要求出在区间 [l,r] 之间的所有数的和。输入格式:第一行包含两个整数 n 和 m。接下来 n 行,每行包含两个整数 x 和 c。再接下来 m 行,每行包含两个整数 l 和 r。输出格式:共 m 行,每行输出一个询问中所求的区间内数字和。数据范围−1e9≤x≤1e9,1≤n,m≤1e5,−1e9≤l≤r≤1e9,−10000≤c≤10000
输入样例:
3 3
1 2
3 6
7 5
1 3
4 6
7 8
输出样例:
8
5
0

代码:#include<bits/stdc++.h>
using namespace std;
int n,m,l,r;
int const N=3e5+10;   //数组里面存x,l,c对应离散化的值,个数最多3*1e5
int a[N],s[N];     
vector<int> lisan;    //存入所有离散化的值
typedef pair<int,int> PII;
vector<PII> addxc,xunwenlr;   //插入x c;  插入询问l r;
int find(int x)     //求离散化的结果
{
    
      int l=0,r=lisan.size()-1;   
    while(l<r)
    {
    
     int mid=l+r >> 1;
         if(lisan[mid]>=x)   r=mid;
         else  l=mid+1;
    }
    return r+1;   //坐标加1,映射到从1开始;
}
int main()
{
    
       cin>>n>>m;
  for(int i=0;i<n;i++)
    {
    
      int x,c;
        cin>>x>>c;
        lisan.push_back(x);
        addxc.push_back({
    
    x,c});
    }
    for(int i=0;i<m;i++)
    {
    
       cin>>l>>r;
        xunwenlr.push_back({
    
    l,r});
        lisan.push_back(l);
        lisan.push_back(r);
    }
      sort(lisan.begin(),lisan.end());
    lisan.erase(unique(lisan.begin(),lisan.end()),lisan.end());    //去重,插入的x l r 去掉相同的
    for(auto item:addxc)    //插入处理
    {
    
       int xx=find(item.first);   //xx离散化所对应的下标
        a[xx]+=item.second;
    }
    for(int i=1;i<=lisan.size();i++) //前缀和
        s[i]=s[i-1]+a[i]; 
    for(auto item:xunwenlr)   //处理询问
    {
    
       l=find(item.first);  //离散化所对应的下标
        r=find(item.second);
        cout<<s[r]-s[l-1]<<endl;
    }         return 0;}

//上面去重unique函数和erase函数 原代码
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];
  return a.begin()+j;
}

unique()函数,后面的元素替换的前面重复的元素,一般排序后使用(去除的是相邻元素),后面的空间并没有删除掉

#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
    
    
	int a[]={
    
    2,3,4,4,6};
	sort(a,a+5);              //一般在使用unique之前都需要进行排序;
	unique(a,a+5);           //使用unique函数对数组进行去重
	for(int i=0;i<5;i++)
	{
    
      cout<<a[i]<<” “;       // 2 3 4 6 6
	}
	cout<<"不重复数列的长度:"<<unique(a,a+5)-a<<endl;   //不重复序列的长度:4 
}

区间合并

// 将所有存在交集的区间合并
void merge(vector<PII> &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;
}

猜你喜欢

转载自blog.csdn.net/weixin_52879528/article/details/122977955