双指针

【双指针】

双指针(two-pointer method)

这里的指针不是指向内存地址的指针,而是一个类似于光标的、指向一个位置的指针。

双指针是一个优化时间复杂度的思想。

【例子1】

两数之和

二重循环当然可以,但是太慢了。

于是我们可以使用双指针优化。

样例:

7 7

1 3 4 8 6 9 5

我们先对这个数组排序。

1 3 4 5 6 8 9

定义两个指针 \(i,j\),初始 \(i=1,j=n\)

枚举 \(i\)\(i\) 表示 “两数中较小那个的下标,每次加一。

随之调整 \(j\)\(j\) 表示 “固定了 \(i\) 之后,使得 \(a[j]+a[i]\leq k\) 最大的 \(j\),一直减到 \(a[j]+a[i]\leq k\)

\(i\geq j\),循环结束。

我们发现,在开始调整了 \(j\) 之后,\(j\) 一定是单调递减的。

这个过程是 \(O(n)\) 的。

【特点】

  1. 属性关于位置单调变化;

  2. 对枚举问题,利用单调性以优化枚举;

  3. 通常枚举一个指针,另一个指针单向维护。

【题目】

数列的三部分

先排序。

还是两个指针指向 \(1,n+1\)

还要 \(sum1=0,sum3=0\),然后在 \(i,j\) 调整时随之调整。

宰牛刀杀鸡

把瓜数组和刀数组排序。

两个指针 \(i,j\) 分别指向两个数组的开头。

每次刀数组的指针加一,另一个指针一直增加到最大的宰牛的瓜。

注意:移动指针时,要判断还有没有下一个。

木板

这题为什么是双指针呢?

两个指针 \(i,j\),表示当前考虑的区间是 \([i,j]\)

一个区间不能变成纯白,等价于这个区间内的黑格子大于 \(m\)

在这个过程中,\(j\) 是单调递增的。

逛画展

单调性:段越长,种类越多。

做一个 \(cnt\) 数组记录。

Equal Cut

双指针,不一定只是“双”指针。

定义三个指针,\(i,j,k\),分出四个子段。

不妨 \(j\leq i \leq k\)

我们发现,当我们枚举 \(i\) 的时候,左右被分成了两段。

左边一段,\(j\) 一定是不变或者右移。

右边一段,\(k\) 也一定是不变或者右移。

\(j,k\) 之间是不会影响的。

最后我们只需要把 \(j,k\) 分出来的四段求极差即可(前缀和)。

Cow Rectangles G

(信息课考试最后一题)

H牛:好牛,G牛:坏牛

第一想法:

枚举上下左右边界,边界上一定有好牛。(不然可以面积更小)

优化:

按 x 排序,选择两只牛做左右边界(\(O(n^2)\))。

把牛复制一遍,按 y 排序(给双指针用)。

每次找好左右边界之后,从上到下找牛(所有牛)。

定义 \(i,j\),表示上下边界。

如果当前牛不在左右边界内,不管;

如果当前牛在边界内,并且是好牛,把 \(j\) 拓展到这里;

如果当前牛在边界内,并且是坏牛,则不能扩张了,\(i\) 从下一个枚举。

但是,如果我们可能会让上下边界没包括左右边界上的牛,对不对?

没问题,因为我们枚举所有牛的时候也包含了左右边界的牛。

但是,我们枚举的 \(i,j\) 代表的牛可能根本用不到左右边界?

没问题,因为我们总有一次会枚举到以 \(i,j\) 之间的牛做左右边界。

不存在,但是不会更新答案的方案,我们不在乎。

【总结】

只要有有序性,就可以考虑双指针。

posted @ 2024-02-15 11:08  FLY_lai  阅读(6)  评论(0编辑  收藏  举报