注:本文是在学习了acwing的算法基础课后撰写,主要用于记录python版本算法的模板。其中部分参考了acwing众多大佬的题解。
1.双指针
思想:
朴素的两重循环需要O(
n
2
n^2
n2),可以利用某种性质(i和j之间的单调关系)将其变为O(n)。
可用于单序列和双序列。
单序列中可用于维护一段区间,双序列中用于维护某种次序,如有序序列的合并。
模板:
# 朴素做法(用于对比):
for i in range(n):
for j in range(i):
if check(j, i):
res = max(res, j - i + 1)
# 双指针做法:
for j in range(n):
while i <= j and check(i, j): # check函数根据题意编写
i += 1
res = max(res, j - i + 1)
2.位运算
常见用法:
1.n的二进制第k位
num >> k & 1
2.返回x的最后一位1
x & (~x+1)
x = 10101000
~x = 01010111
~x+1 = 01011000
x&(~x+1) = 00001000
3.离散化
思想:
适用于数组下标范围大,但值域小的情况。由于数组下标范围太大,开同样大小的数组不现实。离散化可以减少对空间的需求,离散化的思想是将间隔很大的点映射到相邻的数组元素中,常见做法是将数值映射为下标。
如上图,原本需要10001长度的数组,现在只需要4。
步骤:
1.将所有使用到的下标都加入数组adds中
2.对adds排序后去重
3.根据题目要求进行剩余操作
模板:
def find(x):
# 二分寻找
l = 0
r = len(alls) - 1
while l < r:
mid = (l + r + 1) >> 1
if alls[mid] <= x:
l = mid
else:
r = mid - 1
return l + 1
if __name__ == "__main__":
n, m = map(int, input().split())
N = 300010
a = [0] * N # 存储离散化后的索引和对应值,其中索引对应离散化后的索引,值对应离散化前索引的取值
s = [0] * N # 存a数组的前缀和数组
add = [] # 存储插入操作的二元组
query = [] # 存储查询操作的二元组
alls = [] # 存储离散化前输入的所有索引,共n+2m个
# 1.将所有使用到的下标都加入数组adds中
for i in range(n):
x, c = map(int, input().split())
add.append((x, c))
alls.append(x)
for j in range(m):
l, r = map(int, input().split())
query.append((l, r))
alls.append(l)
alls.append(r)
# 2.alls排序并去重
alls = list(set(sorted(alls)))
# 3.根据题目要求进行剩余操作
# 插入
for x, c in add:
x2 = find(x)
a[x2] += c
# 前缀和
for i in range(1, len(alls) + 1):
s[i] = s[i-1] + a[i]
# 查询
for l, r in query:
l2 = find(l)
r2 = find(r)
res = s[r2] - s[l2-1]
print(res)
4.区间合并
思想:
用于快速合并n个区间。先排序后遍历维护合并区间。
步骤:
1.以区间左端点为基准对区间排序。
2.按顺序遍历区间,维护区间[start,end],表示当前的合并区间。若遍历到的区间与当前合并区间有交集,就会将其合并,否则遍历到的区间成为新的合并区间。
模板:
"""
segs为区间数组如[(1,2), (1,4)]
res为合并后的区间数组如[(1,4)]
"""
def merge(segs):
res = []
segs.sort()
start = float('-inf')
end = float('-inf')
for l, r in segs:
if end < l:
if start != float('-inf'):
res.append((start, end))
start = l
end = r
else:
end = max(end, r)
if start != float('-inf'):
res.append((start, end))
return res