双指针
【双指针】
双指针(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,n+1\)。
还要 \(sum1=0,sum3=0\),然后在 \(i,j\) 调整时随之调整。
把瓜数组和刀数组排序。
两个指针 \(i,j\) 分别指向两个数组的开头。
每次刀数组的指针加一,另一个指针一直增加到最大的宰牛的瓜。
注意:移动指针时,要判断还有没有下一个。
这题为什么是双指针呢?
两个指针 \(i,j\),表示当前考虑的区间是 \([i,j]\)。
一个区间不能变成纯白,等价于这个区间内的黑格子大于 \(m\)。
在这个过程中,\(j\) 是单调递增的。
单调性:段越长,种类越多。
做一个 \(cnt\) 数组记录。
双指针,不一定只是“双”指针。
定义三个指针,\(i,j,k\),分出四个子段。
不妨 \(j\leq i \leq k\)。
我们发现,当我们枚举 \(i\) 的时候,左右被分成了两段。
左边一段,\(j\) 一定是不变或者右移。
右边一段,\(k\) 也一定是不变或者右移。
而 \(j,k\) 之间是不会影响的。
最后我们只需要把 \(j,k\) 分出来的四段求极差即可(前缀和)。
(信息课考试最后一题)
H牛:好牛,G牛:坏牛
第一想法:
枚举上下左右边界,边界上一定有好牛。(不然可以面积更小)
优化:
按 x 排序,选择两只牛做左右边界(\(O(n^2)\))。
把牛复制一遍,按 y 排序(给双指针用)。
每次找好左右边界之后,从上到下找牛(所有牛)。
定义 \(i,j\),表示上下边界。
如果当前牛不在左右边界内,不管;
如果当前牛在边界内,并且是好牛,把 \(j\) 拓展到这里;
如果当前牛在边界内,并且是坏牛,则不能扩张了,\(i\) 从下一个枚举。
但是,如果我们可能会让上下边界没包括左右边界上的牛,对不对?
没问题,因为我们枚举所有牛的时候也包含了左右边界的牛。
但是,我们枚举的 \(i,j\) 代表的牛可能根本用不到左右边界?
没问题,因为我们总有一次会枚举到以 \(i,j\) 之间的牛做左右边界。
不存在,但是不会更新答案的方案,我们不在乎。
【总结】
只要有有序性,就可以考虑双指针。