注:这个算法本质上是一个贪心技巧,而且命名由作者自拟,读者可以根据自身理解来为算法命名。
问题引入
一道耳熟能详的问题:硬币翻转。
题意如下:
- 有 $n$ 个硬币按顺序摆在桌上,每个硬币可能是正面朝上 $(a_i=1)$,也有可能是反面朝上 $(a_i=2)$。
- 每次选取连续的 $k$ 个硬币,将它们翻转。
- 问最少翻转多少次可以将所有硬币变成正面朝上,如果不可能实现输出 $-1$。
- $1 le k le n le 5 imes 10^6$,$a_i in {1,2}$。
简单分析
我们可以先观察一下题目。
容易发现,如果两次翻转的是同一个区间的硬币,那么正面朝上的硬币还是正面朝上的硬币(反面同理),故没有效果。
所以,如果可以实现目的,翻转的硬币区间一定是不同的。
那一个长度为 $n$ 的序列选出长度为 $k$ 的子串,最多只有 $n-k+1$ 个不同的方案,而且翻转操作不会应操作的顺序受到影响。
于是这道题,可做了。
何为推平
这 $n-k+1$ 个子区间如果乱选,时间复杂度极高,为 $O(2^{n-k+1} imes nk)$。
这时我们就应该找到一个突破口。
如果我们要翻转 $1$ 号硬币,那只能翻转区间 $[1,k]$,然后 $1$ 号硬币归位;
然后如果这时 $2$ 号硬币原来是正的,被翻转一次后变反了,因为不能修改同一个子区间,我们只能把区间 $[2,k+1]$ 翻转。
……以此类推。
最后,如果有解,答案一定是执行翻转操作的次数。
正确性
正确性其实已经很明显了,因为如果 $1$ 号硬币至 $b$($b$ 是一个 $[1,n]$ 间的整数)都是正面,再将它们反转一定不是最优的。
所以,这个做法在每一个环节都是最优的。
算法优化
上述做法还是 $O(n^2)$ 的,如何优化?
其实看到翻转操作,就应该知道这么做了。
将区间内所有正变反,反变正,明显是区间异或操作,因为没有修改,所以可以用差分解决。
时间复杂度降为 $O(n)$。
何时无解
注意到,这题可能存在无解的情况。
那这就很容易了,如果翻转到第 $n-k+1$ 个子区间(就是最后一个区间)后还没有把所有硬币变成正的,这时就会无解。