分治补充题目
-
区间最大子段和
-
当然可以使用动态规划,复杂度 O ( n ) O(n) O(n), d p [ i ] dp[i] dp[i] 表示以 a [ i ] a[i] a[i] 为结尾的最长子段
d p [ i ] = max ( d p [ i − 1 ] + a [ i ] , a [ i ] ) dp[i]=\max(dp[i-1]+a[i],a[i]) dp[i]=max(dp[i−1]+a[i],a[i]) -
但是这里我们考虑分治的想法,对于一段区间 [ l , r ] [l,r] [l,r] , 考虑 m i d = ( l + r ) / 2 mid=(l+r)/2 mid=(l+r)/2 ,该区间被分为左右两段, [ l , m i d ] , [ m i d + 1 , r ] [l,mid],[mid+1,r] [l,mid],[mid+1,r] ,总区间的最大子段和只有三种情况,左、右、跨越两段。问题就可以转化为与课上讲的平面最近点对类似的子问题模式。分别在左右就是两个完全相同的子问题,直接递归;跨越中间的部分,则由左半段从右端开始向左的最大子段和右半段从左端开始向右的最大子段
-
考虑复杂度
T ( n ) = 2 ∗ T ( n 2 ) + L ( n 2 ) + R ( n 2 ) L ( n ) = L ( n 2 ) + L ( n 2 ) + O ( n ) \begin{aligned} T(n)&=2*T\left(\frac{n}{2}\right)+L\left(\frac{n}{2}\right)+R\left(\frac{n}{2}\right)\\ L(n)&=L\left(\frac{n}{2}\right)+L\left(\frac{n}{2}\right)+O(n) \end{aligned} T(n)L(n)=2∗T(2n)+L(2n)+R(2n)=L(2n)+L(2n)+O(n)
在这种情况下,由主定理容易得出 L ( n ) = R ( n ) = Θ ( n l o g n ) , T ( n ) = O ( n log 2 n ) L(n)=R(n)=\Theta(nlogn),\ T(n)=O(n\log^2 n) L(n)=R(n)=Θ(nlogn), T(n)=O(nlog2n)
-
这里的 O ( n ) O(n) O(n) 可以通过提前 O ( n ) O(n) O(n) 预处理区间的前缀和降低为 O ( 1 ) O(1) O(1) ,这样 L ( n ) = O ( n ) , T ( n ) = O ( n ) + O ( n log n ) L(n)=O( n),\ T(n)=O(n)+O(n\log n) L(n)=O(n), T(n)=O(n)+O(nlogn) ,看似复杂度甚至还不如动态规划,但当我们可以结合线段树等数据结构 O ( log n ) O(\log n) O(logn) 动态维护区间前缀和的情况下,我们就能在总复杂度 O ( m log n ) O(m\log n) O(mlogn) 的情况下动态的维护区间最大子段和,而这是传统动态规划做不到的
-
-
漂亮数组
-
给定 n n n ,若一个1到n的排列满足 ∀ i , j \forall i,j ∀i,j 满足不 ∃ k ( i < k < j ) \exist k\ (i<k<j) ∃k (i<k<j) ,满足 a [ i ] + a [ j ] = 2 ∗ a [ k ] a[i]+a[j]=2*a[k] a[i]+a[j]=2∗a[k] ,那么这个数组是漂亮的。现在给定n,假设漂亮数组存在,求出一个合法的漂亮数组。
-
性质:若 [ a 1 , a 2 , ⋯ , a n ] [a_1,a_2,\cdots,a_n] [a1,a2,⋯,an] 是漂亮的,则 [ k a 1 + b , k a 2 + b , ⋯ , k a n + b ] [ka_1+b,ka_2+b,\cdots,ka_n+b] [ka1+b,ka2+b,⋯,kan+b] 也是漂亮的。证明利用反证是容易的。
-
构造思路: 将数组分成左右两部分,分别求出一个漂亮的数组,然后将它们进行变换,使得不存在满足下面条件的三元组:
A [ m ] ∗ 2 = A [ i ] + A [ j ] , i < m < j A [ m ] ∗ 2 = A [ i ] + A [ j ]\ ,\ i < m < j A[m]∗2=A[i]+A[j] , i<m<j
A [ i ] A[i] A[i] 来自左半部分, A [ j ] A[j] A[j] 来自右半部分A [ m ] ∗ 2 = A [ i ] + A [ j ] A[m]*2=A[i]+A[j] A[m]∗2=A[i]+A[j] 的左侧是一个偶数,右侧的两个元素分别来自两个部分,想要保证 A [ i ] + A [ j ] A[i]+A[j] A[i]+A[j] 不为偶数,只要一奇一偶即可,一个直观的想法就是把奇数、偶数分别放在两侧。
对于一个 { 1 , 2 , ⋯ , n } \{1,2,\cdots,n\} {1,2,⋯,n} 的排列,左半部分包含 ⌊ ( n + 1 ) / 2 ⌋ ⌊(n+1)/2⌋ ⌊(n+1)/2⌋ 个奇数,右半部分含 ⌊ n / 2 ⌋ \lfloor n/2\rfloor ⌊n/2⌋个偶数
左半部分进行 k = 1 / 2 , b = 1 / 2 k=1/2,b=1/2 k=1/2,b=1/2 的变换,右半部分进行 k = 1 / 2 k=1/2 k=1/2 的变换,这样就变成了同样的子问题,分治解决
-
-
多数元素
-
给定一个大小为 n n n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 $ \lfloor n/2 \rfloor$ 的元素(假设多数元素一定存在)。
-
将数组拆分为左右两半,分以下三种情况:
- 只有一个数,显然为区间众数
- 左右多数元素相同,则整段区间的多数元素必然也是这个数
- 左右多数元素不同,遍历区间比较这两个“多数元素”的出现次数
-
复杂度: T ( n ) = 2 T ( n / 2 ) + O ( n ) T(n)=2T(n/2)+O(n) T(n)=2T(n/2)+O(n) ,由主定理容易推出 T ( n ) = Θ ( n log n ) T(n)=\Theta(n\log n) T(n)=Θ(nlogn)
-
但其实分治并不是最优的解法,考虑 Boyer-Moore 投票算法
-
我们维护一个候选众数 candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count 为 0;
-
我们遍历数组中的所有元素,对于每个元素 x,在判断 x 之前,如果 count 的值为 0,我们先将 x 的值赋予 candidate,随后我们判断 x:
如果 x 与 candidate 相等,那么计数器 count 的值增加 1
如果 x 与 candidate 不等,那么计数器 count 的值减少 1
-
遍历结束后,candidate即为多数元素
-
-
原理:不妨假设整个数组的众数记做a,则最初的数组中a的数量大于其余所有数。当采用count计数的时候有两种情况:
1)假设 candidate 等于a,则当 count 从1变为0的过程,此区间内a的数量等于其余数的数量,因此以 count=0 为分界线,数组右端部分的众数仍然为a
2)假设 candidate 不等于a,则当 count 从1变为0的过程, 此区间内a的数量小于等于其余数的数量,因此以 count=0 为分界线,数组右端部分的众数仍然为a
-
时间复杂度 O ( n ) O(n) O(n) ,空间复杂度 O ( 1 ) O(1) O(1)
-
-
如果还有时间:啤酒瓶问题