1.双指针
双指针的思路是从一个暴力二重循环优化得来的,使得其中一个指针不再向反方向走即可(但是其实有时候挺难想出来的)
双指针算法并没有固定的模版,具体的题目需要具体对待,下面放一道经典的题目当作模板吧
数组元素的目标和(awcing.800)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int x, n, m;
int A[N], B[N];
int main()
{
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]);
for(int i = 0, j = m - 1; i < n ; i ++)
{
while(A[i] + B[j] > x && j >= 0) j --;
if(A[i] + B[j] == x)
{
printf("%d %d", i, j);
break;
}
}
return 0;
}
2.位运算
位运算其实并不能算一个算法,只能说是一个操作,可以用来枚举操作等
二进制中1的个数 (acwing.801)
#include <bits/stdc++.h>
using namespace std;
int lowbit(int x)
{
return x & -x;
}
int main()
{
int n;
cin >> n;
for(int i = 0 ; i < n; i ++)
{
int a;
scanf("%d", &a);
int res = 0;
while(a) a -= lowbit(a), res ++;
printf("%d ", res);
}
}
3.离散化
离散化很像前缀和操作,可以理解为在稀疏条件下的前缀和(其实我觉得叫做连续化会更好理解一点)
具体的思路就是把所有的距离比较远的坐标重新映射到新的数组中,把稀疏变成稠密
区间和(acwing.802)
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int n, m;
int a[N], s[N];
vector<int> alls; //下标容器
vector<PII> add, query; //add增加容器,存入对应下标和增加的值的大小
//query储存需要计算的下标区间的容器
int find(int 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开始,这里要加上1
}
int main()
{
cin >> n >> m;
//读入对每一个坐标的操作
for(int i = 0; i < n; i ++)
{
int x, c; //操作的数字下标和操作的值
cin >> x >> c;
add.push_back({x, c}); //存入数组下标即对应的数值C
alls.push_back(x); //存入下标x = add.first
}
//处理query
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());
//处理插入
for(auto item : add)
{
int x = find(item.first); //将add容器的add.second值存入数组a[]中†
a[x] += item.second; //去重以后的下标集合alls内寻找对应的下标并添加数值
}
//预处理前缀和
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); //在影射的数组内查找对应的区间
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
4.区间合并
区间合并有点贪心内味,主要目的就是把有交点的两端区间合并
区间合并(acwing.803)
#include <bits/stdc++.h>
using namespace std;
const int N = -2e9;
#define x first
#define y second
typedef pair<int, int> PII;
int n;
vector<PII> segs;
void merge()
{
vector<PII> res;
sort(segs.begin(), segs.end());
int st = N, ed = N;
for(auto seg : segs)
{
if(ed < seg.x) //这里不能加等号,因为如果下一个区间的左端点和右端点相等的话,
//说明上一个区间是包含它的,不需要进行操作
{
if(st != N) res.push_back({st, ed});
st = seg.x, ed = seg.y;
}
else ed = max(ed, seg.y);
}
if(st != N) res.push_back({st, ed}); //压入最后一个区间
segs = res;
}
int main()
{
cin >> n;
for(int i = 0 ; i < n ; i++)
{
int l, r;
scanf("%d%d" ,&l, &r);
segs.push_back({l, r});
}
merge();
cout << segs.size() << endl;
return 0;
}