[AcWing],双指针算法,位运算,离散化与区间合并

问题来源 : ACWing
https://www.acwing.com/blog/content/277/

双指针

最长连续不重复子串长度

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

const int N = 100010;
int f[N];

int main() {
    int n;
    cin >> n;
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    
    int ans = 0;
    int le = 0;
    set<int> st;
    for(int i = 0; i < n; i++) {
        if(st.count(f[i])) {
            while(le < i && f[le] != f[i])
                st.erase(f[le++]);
            le++;
        } else {
            st.insert(f[i]);
        }
        ans = max(ans,i - le + 1);
    }
    cout << ans << endl;
    return 0;
}

数组元素的目标和

#include <iostream>
using namespace std;

const int N = 100010;
int f[N],s[N];
int n,m,x;

int main() {
    cin >> n >> m >> x;
    for(int i = 0; i < n; i++) scanf("%d",&f[i]);
    for(int i = 0; i < m; i++) scanf("%d",&s[i]);
    
    for(int i = 0, j = m - 1; i < n; i++) {
        while(j >= 0 && f[i] + s[j] > x) j--;
        if(j >= 0 && f[i] + s[j] == x) { cout << i << " " << j << endl;break;}
    }
    
    // 二分
    // int a,b;
    // for(int i = 0; i < n; i++) {
    //     int num = x - f[i];
    //     int le = 0;
    //     int ri = m - 1;
    //     while(le < ri) {
    //         int mid = (le + ri) / 2;
    //         if(s[mid] < num) le = mid + 1;
    //         else if(s[mid] >= num) ri = mid;
    //     }
        
    //     if(num == s[le]) {
    //         cout << i << " "<<le << endl;
    //         break;
    //     }
    // }
    return 0;
}

二进制中1的个数

在这里插入图片描述

负数用补码表示的原因:

对于一个正数 X,那么对于他的相反数-X,一定有以下的关系X + (-X) = 0

那么 -x = 0 - x
在这里插入图片描述

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int f[N];
int n,m;

int slove() {
    int cnt = 0;
    while(m != 0) {
        cnt ++;
        m -= m & (-m); // -m == ~m + 1  lowbit(x)
    }
    return cnt;
}

int main() {
    cin >> n;
    for(int i = 0; i < n; i++) {
        cin >> m;
        f[i] = slove();
    }
    
    for(int i = 0; i < n; i++) printf("%d ",f[i]);
    return 0;
}

区间和

在这里插入图片描述
因为题中的要求是在一个坐标轴中,每次对于某一个下标进行操作+c,-c,并且坐标是存在负数的,范围还是-1e9 ~ 1e9,但是实际用到的点最多3 * 1e5个,就需要进行一次离散化。

  1. 读输入。将每次读入的x cpush_back()到add中,将每次读入的位置x push_back()到all中,将每次读入的l r push_back()到query中。
  2. 排序、离散化去重。
  3. 通过遍历add,完成在离散化的数组映射到的t数组中进行加上c的操作,并进行差分
  4. 求前缀和。
  5. 通过遍历query,完成求区间[l,r]的和。

至于去重,就是为了让整个使用到的点的排列更紧促一点,让在二分查找的时候不会出现对重复元素的查找,相对来说能快一点。C++中对于去重可以使用unique函数,函数的返回值是所有不重复的区间的下一个位置的元素。
在这里插入图片描述

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

using namespace std;

const int N = 3 * 1e5 + 10;
int n,m;
int x,c;
int l,r;
int f[N],t[N];      // 前缀和数组

typedef pair<int,int> PII;
vector<int> all;    // 所有需要用到的点
vector<PII> add,query;     // 需要操作的点,与需要求结果的区间

int find(int x) {
    l = 0, r = all.size();
    while(l < r) {
        int mid = (l + r) >> 1;
        if(all[mid] >= x) r = mid;
        else if(all[mid] < x) l = mid + 1;
    }
    return l + 1;  // 返回前缀和时数据的下标
}

vector<int>::iterator my_unique() {
    int j = 1;
    for(int i = 1; i < all.size(); i++) {
        if(all[j - 1] != all[i])    all[j++] = all[i];
    }
    return all.begin() + j;
}

int main() {
    cin >> n >> m;
    for(int i = 0; i < n; i++) {
        cin >> x >> c;
        add.push_back(make_pair(x,c));
        all.push_back(x);
    }
    for(int i = 0; i < m; i++) {
        cin >> l >> r;
        query.push_back(make_pair(l,r));
        all.push_back(l);
        all.push_back(r);
    }
    
    // 离散化 + 去重
    sort(all.begin(),all.end());
    all.erase(my_unique(),all.end());
    //all.erase(unique(all.begin(),all.end()),all.end());
    
    // 差分
    for(auto item : add) {
        int idx = find(item.first);
        t[idx] += item.second;
    }
    // 前缀和
    for(int i = 1; i <= all.size(); i++) f[i] = t[i] + f[i-1];
    
    // 结果
    for(auto item : query) {
        int le = find(item.first), ri = find(item.second);
        cout << f[ri] - f[le - 1] << endl;
    }
    return 0;
}

合并区间

在这里插入图片描述

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

using namespace std;

const int N = 1e5 + 10;
typedef pair<int,int> PII;
vector<PII> add;
int n,ans;

void merage() {
    sort(add.begin(),add.end());
    int pre = add[0].second;
    for(int i = 1; i < n; i++) {
        if(add[i].first > pre) {
            pre = add[i].second;
            ans++;
        } else {
            pre = max(add[i].second,pre);
        }
    }
    ans ++;
}

int main() {
    cin >> n;
    for(int i = 0; i < n; i++) {
        int x,y;
        cin >> x >> y;
        add.push_back(make_pair(x,y));
    }
    
    merage();
    
    cout << ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值