题意
给定一个仅包含$0$和$1$的数组,仅能交换其中相邻的两个数字,问至少多少次这样的操作可以使得至少$k$个$1$相邻。
题解
取数组中任意$k$个相距最近的$1$,设它们出现的下标分别为$a_1,a_2,…,a_k.$考虑将它们交换多少次可以使得它们聚成连续的$k$个$1$。有这样一个常用结论:对于数轴上的点$a_1,a_2,…,a_k$,使它们移动到一处的最小代价当它们都移动到该序列的中位数时取得(若长度为偶数,左边的和右边的均可)。按照这个结论,我们要将所有的$1$都移动到$a_1,a_2,…,a_k$的中位数旁边。先讨论
$(1)$长度$k=2n+1$即$k$为奇数的情况:
要将$a_1,a_2,…,a_{n+1},…,a_{2n},a_{2n+1}$移动到$a_{n+1}$处,
首先将$a_{n},a_{n+2}$移动到$a_{n+1}$的左右两边,其消耗为$(a_{n+1}-a_n-1)+(a_{n+2}-a_{n+1}-1);$
再将$a_{n-1},a_{n+3}$分别移动到$a_{n},a_{n+2}$的两边,消耗为$(a_{n+1}-a_{n-1}-2)+(a_{n+3}-a_{n+1}-2);$
以此类推,可以求得消耗之和为
$\sum_{i=1}^{2n+1}|a_{n+1}-a_{i}|-2(1+2+…+n)=\sum_{i=1}^{2n+1}|a_{n+1}-a_{i}|-n(n+1).$
$(1)$长度$k=2n$,即$k$为偶数的情况:
用与奇数的情况类似的方法推导,可以得到消耗之和为$\sum_{i=1}^{2n}|a_{n+1}-a_{i}|-2(1+2+…+(n-1))-n=\sum_{i=1}^{2n}|a_{n+1}-a_{i}|-n^2.$
因此只需用一个滑动窗口从序列的左边扫到右边,使得窗口中始终含有$k$个$1$,同时维护消耗值即可。观察到消耗值中$n(n+1)$(或$n^2$)与序列中的值无关,最后减去即可,于是重点就在于维护绝对值之和的项(记为$abssum$)。
考虑移动过程中$abssum$的变化:
先讨论$k=2n+1$时的情况。设序列由
$a_1,a_2,…,a_{2n+1}$滑动到$a_2,a_3,…,a_{2n+1},a_{2n+2}.$
原来的$abssum_0=(a_{n+1}-a_1)+(a_{n+1}-a_2)+…+(a_{n+1}-a_n)+(a_{n+2}-a_{n+1})+…+(a_{2n+1}-a_{n+1});$
滑动后的$abssum’=(a_{n+2}-a_2)+(a_{n+2}-a_3)+…+(a_{n+2}-a_{n+1})+(a_{n+3}-a_{n+2})+…+(a_{2n+2}-a_{n+2}).$
两式相减得
$abssum’-abssum_0=a_{2n+2}+a_1-a_{n+1}-a_{n+2},$即两边的值减去新旧中位数之和。
同理可以求得,当$k=2n$时,$abssum’-abssum_0=a_{2n+1}+a_1-2a_{n+1},$即两边的值减去原序列右边的中位数的二倍。(这里在推导时,$abssum_0$用的中位数和$abssum’$用的中位数都是$a_{n+1}$)因此只需在滑动过程中维护新旧中位数、新添加进来的$1$和即将被删掉的$1$即可。由于各个变量至多从$0$移动到$num.size()-1,$故时间复杂度$O(n).$
代码
1 | class Solution { |