zoukankan      html  css  js  c++  java
  • 「基础算法」贪心 ——“推平”法

    注:这个算法本质上是一个贪心技巧,而且命名由作者自拟,读者可以根据自身理解来为算法命名。

    问题引入

    一道耳熟能详的问题:硬币翻转。

    题意如下:

    • 有 $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$ 个子区间(就是最后一个区间)后还没有把所有硬币变成正的,这时就会无解。

    推荐题目

    洛谷OJ P6070 『MdOI R1』Decrease

  • 相关阅读:
    2014--My Plan
    C++操作xls
    fail2ban[防止linux服务器被暴力破解]
    8.1向上转型
    寒假的一个月--实现自己的五部狂奏曲
    浅谈《think in java》:二 一切都是对象
    浅谈《think in java》:一 对象导论总结
    增加Myecllipse内存
    java 容器 集合 用法
    SVN用户切换
  • 原文地址:https://www.cnblogs.com/zengpeichen/p/13765934.html
Copyright © 2011-2022 走看看