基础算法(I‘m蒟蒻)

  • 双指针算法:将时间复杂度为O(n^2)的算法转化为O(n) 

     例如可将如下算法:

for (int i = 0; i < n ; i ++ )
    for (int j = 0; j < n; j ++)
         check(i, j)
    

 转化为如下时间复杂度为O(n)的算法

for (int i = 0; i < n; i ++ )
{
    while (j < i && check(i, j))
    // 执行相应操作
}

常见问题题型

  •  对于一个序列,用两个指针维护一段区间
  • 对于两个序列,维护某种次序,例如归并排序将两个序列合并为一个

  一个典型例题: acwing 799 最长连续不重复子序列

算法核心:将 i 从头枚举到尾, j 始终在 i 的左边 ,当j < i并且 j到i的区间内无重复元素时进行 j++,最后的停下的 j 即为 在 i  左边 的最长无重复子区间 ,并记录,比较直至循环结束

  •  位运算

>> 右移运算符 :将二进制数向右移动,左边移出的空位补0或补符号位 (有符号位补符号位,无符号位补0)
<< 左移运算符:将二进制数向左移动,右边移出的空位补0

常见题型

1) 求n的第k位数: n >> (k - 1) & 1 

2) 返回n的最后一位1(最右边的一位1也就是说最低位的一位1) :lowbit(n) = n & - n

      在c++中 一个数n的负数为n的取反加1(即为n的补码) 即: -n = ~n + 1 (可列举几个数进验证)

一个典型例题:acwing 801. 二进制中1的个数

核心算法:对于一个数n,在while(n) 的条件下,每次成功调用lowbit函数,记录res。并将n减去lowbit(n)

  • 区间合并:将所有有交集的区间合并

算法思路:将所有区间按区间起始点大小排序,将每个区间的端点存入pair数组中,新建起始点和结尾点根据此时所在的区间进行不断更新。

例题:acwing 803 区间合并

代码如下:

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

typedef pair<int, int>PII;
int n;

void merge(vector<PII> &segs)
{
    vector<PII>res;
    sort(segs.begin(), segs.end());
    
    int st = -2e9, ed = -2e9;
    for(auto seg : segs)
    {
        if (seg.first > ed)
        {
            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;
    int l, r;
    vector<PII>segs;
    
    for (int i = 0; i < n; i ++ )
    {
        cin >> l >> r;
        segs.push_back({l, r});
    }
    
    merge(segs);
    
    cout << segs.size();
    return 0;
}

最重要的是如何设置和更新尾端点

  • 离散化

定义: 将长数轴上稀疏的点进行映射,缩小范围,从而达到减小时间复杂度

核心操作: 1)将点存入一个vector容器中,按点的下标x大小对容器进行排序(所有点的相对顺序不变

2)根据题目的要求,容器中可能存入多个相同的点,进行去重 

3)若对这些点进行一些操作利用find(x) 找到x对应于新的容器内的下标(二分

例题: acwing 803 区间和

代码如下:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

const int N = 3e6;
typedef pair<int, int> PII;

vector<int> segs;
vector<PII> args, ergs;

int arr[N], s[N];
int n, m;

int find(int x)
{
    int l = 0, r = segs.size() - 1;
    while(l < r)
    {
        int mid = (l + r) >> 1;
        if (segs[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;
        scanf("%d%d", &x, &c);
        
        segs.push_back(x);
        args.push_back({x, c});
    }
    
    for (int i = 0; i < m; i ++ )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        
        ergs.push_back({l, r});
        segs.push_back(l);
        segs.push_back(r);
    }
    
    sort(segs.begin(), segs.end());
    segs.erase(unique(segs.begin(), segs.end()), segs.end());
    
    for (auto arg : args)
    {
        int ans = find(arg.first);
        arr[ans] += arg.second;
    }
    
    for (int i = 1; i <= segs.size(); i ++ ) s[i] = s[i - 1] + arr[i];
    
    for (auto erg : ergs)
    {
        int l = find(erg.first), r = find(erg.second);
        cout << s[r] - s[l - 1] << endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值