0x01 基础算法

基础算法

排序

快速排序

思想:分治

  1. 确定分界点:q[l],q[(l+r)/2],q[r]
  2. 调整范围:q[]<=x,q[]>=x
  3. 递归处理左右两段
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];
    //不能令 mid=l+r>>1 因为 q[mid] 可能会被交换
    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);
    //用 j 做边界时,x 不能取 q[r]; 用 i 做边界时,x 不能取 q[l] (stagement fault)
    quick_sort(q, j + 1, r);
}

归并排序

分治

  1. 确定分界点 mid=(l+r)/2
  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]) temp[k++]=q[i++];
    	else temp[k++]=q[j++];
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    for(i=l,j=0;i<=r;i++,j++) q[i]=temp[j];
}

二分

整数二分

  1. 本质:边界,某一性质在左半边满足,在右半边不满足
  2. 步骤
    1. mid二分,判断mid点是否满足
    2. 选择区间,判断是否包括边界
    3. 递归得到答案
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)//也可以写 for(int i=0;i<100;i++) ,即将范围除以 2^100 ,精度足够高
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

高精度

输入输出

string a,b;
vector<int>A,B;
cin>>a>>b;//a="123456"
for(int i=a.size()-1;i>=0;i--)
    A.push_back(a[i]-'0');//A = [6,5,4,3,2,1]
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]);

高精度加法

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

高精度减法

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)
{
    if(!cmp(A,B))
    {
        cout<<"-";
        return sub(B,A);
    }
    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;
}

高精度乘法

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

高精度除法

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;
    }//aaaaa/bb=00cdef
    reverse(C.begin(), C.end());//fedc00
    while (C.size() > 1 && C.back() == 0) C.pop_back();//fdec
    return C;
}

前缀和

前缀和:\(S_i=a_1+a_2+...a_i\)

for(i=1;i<=n;i++)//求前缀和
    S[i]=S[i-1]+a[i];
cin>>a>>b;cout<<S[b]-S[a-1]<<endl;//算部分和
  • \(\sum_{i=a}^b{a_i}=S_b-S_{a-1}\)

  • 前缀和能快速计算区间和a[l]~a[r],累加复杂度O(n) ,而前缀和复杂度O(1)

  • 首项为\(a_1\)能更好地处理边界问题,令\(S_0=0\),则\(\sum_{i=1}^n{a_i}=S_n-S_0\)

二维前缀和:\(S_{x,y}=\sum_{i=1}^x{\sum_{j=1}^y{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];
cin>>x1>>y1>>x2>>y2;
cout<<S[x2][y2]-S[x1-1][y2]-S[x2][y1-1]+S[x1-1][y1-1]<<endl;//算部分和
  • \(\sum_{i=x_1}^{x_2}{\sum_{j=y_1}^{y_2}{a_{i,j}}}=S_{x_2,y_2}-S_{x_2,y_1-1}-S_{x_1-1,y_2}+S_{x_1-1,y_1-1}\)

差分

前缀和的逆运算

差分:构造\({\{b_n\}}\)使得\(a_n=\sum_{i=1}^n{b_i}\),即\(b_i=a_i-a_{i-1}\),则\({\{b_n\}}\)\({\{a_n\}}\)的差分,\({\{a_n\}}\)\({\{b_n\}}\)的前缀和

  • 差分能快速进行区间更新,将复杂度从O(n)降到O(1)
//为区间 [l,r] 的所有元素加上 c
B[l]+=c;B[r+1]-=c;
//通过差分求原数列
for(int i=1;i<=n;i++)
{
	b[i]+=b[i-1];
    cout<<b[i];
}

二维差分:\(b_{i,j}=a_{i,j}-a_{i-1,j}-a_{i,j-1}+a_{i-1,j-1}\)

//为区间 (x1,y1) 到 (x2,y2) 的所有元素加上 c
B[x1,y1]+=c,B[x2+1,y1]-=c,B[x1,y2+1]-=c,B[x2+1,y2+1]+=c;
//通过差分求原数列
for(int i=1;i<=n;i++)
{
	b[i]+=b[i-1];
    cout<<b[i];
}
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];
        cout<<b[i][j]<<" ";
    }
    cout<<endl;
}

双指针算法

  1. 核心思想:两个指针同时操作数据结构,将复杂度从\(O(n^2)\)降至\(O(n)\)
for(i=0,j=0;i<n;i++)
{
	while(j<i&&check(i,j)) j++;
    //每道题具体逻辑
}

位运算

n>>k&1;//n 的二进制表示的第 k 位
for(int k=n;k>=0;k--) cout<<(n>>k&1);//转换为二进制表示
lowbit(x)=x&-x;//返回 x 的最后一位 1
while(x) x -=(x & -x),tot++;//统计 x 中 1 的个数

离散化

将大范围数据映射到小范围数据

vector<int> alls; // 存储所有待离散化的值
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
}

区间合并

快速合并有交集的区间

  1. 按区间左端点排序
  2. 从左往右维护区间,若下一区间左端点小于等于该区间右端点,归并区间;若下一区间左端点大于等于该区间右端点,纳入答案
typedef pair<int,int> PII;
const int M = -2e9;//边界
void merge(vector<PII> &segs)
{
    vector<PII> res;
    sort(segs.begin(), segs.end());
    int st = M, ed = M;
    for (auto seg : segs)
        if (ed < seg.first)
        {
            if (st != M) res.push_back({st, ed});
            st = seg.first, ed = seg.second;
        }
        else ed = max(ed, seg.second);
    if (st != M) res.push_back({st, ed});
    segs = res;
}
posted @ 2025-02-27 09:38  YamadaRyou  阅读(27)  评论(0)    收藏  举报