贪心基础 - 学习笔记

贪心基础 - 学习笔记

代码集合

主要记录下做到的贪心题

* 的是我认为有一定难度的题,* 越多难度越大

持续施工中……

贪心整合包 (easy)

ABC103D

题意为给定 m 个区间 [li,ri],要求 li,li+1,,ri 这些点中至少有一个被删掉,在满足所有要求的同时最小化删除点数量,求最终的删除点数量

转化为经典区间覆盖模型

这是一个比较典的贪心模型,我们将所有区间 按照右端点排序 (按照左端点排序可能出现一个区间完全包含另一个区间的情况,不好分析,因此选择按右端点排序),贪心可知对于每个区间,若其不满足要求则 删去最右端的点 一定最优

实现时维护最靠右的删除点位置即可

ABC137D

转化下题意,给定 m 项一次性工作,每个工作有截止日期 ai 与报酬 bi,工作当天干完 (一天只能干一个),只要在截止日期(及)之前完成工作就可以拿到报酬,问能拿到的最大报酬

无法直接确定当前工作干或不干 (有截止日期限制,不好定序) 悔贪,维护最劣操作替换

很典的悔贪,我们将工作按照截止日期排序,建立 小根堆 维护之前选择干的工作;对于每个工作:

  1. 如果在其截止日期前能干掉(即小根堆元素个数 <ai)则直接将其报酬插入堆中
  2. 反之,取出小根堆堆顶 v,若 v<bi 则说明将之前的工作替换为当前工作更优,ansans+biv,弹出堆顶并将 bi 插入堆中

ABC123D

题意为给定三个数组 ai,bi,ci,将所有 ai+bj+ck 从大到小排序后输出前 k

数太多没法直接排序 考虑从已知数推出下一个数

容易发现一个感性理解很对的做法:将 ai,bi,ci 从大到小排序,建立大根堆,首先将 a1+b1+c1 插入堆中,之后重复执行 k 次如下操作:

  1. 输出堆顶并将其弹出
  2. 设堆顶为 ai+bj+ck,将 ai+1+bj+ck,ai+bj+1+ck,ai+bj+ck+1 插入堆中

由于总共只会向堆中加入 3k 个数,复杂度没有问题

考虑简单证明,设堆顶包含 ai,下一个满足要求的数显然不会跳过 ai+1ai+2,将 ai+1 入队的正确性可以保证(其余两个数组同理);另外,显然我们这么操作保证了不会有更大的三个数之和不在堆中,因此一定能取到满足要求的数

P3619

题意为给定 T,有 n 个任务,每个任务有限制 ti 与报酬 bi,只有 T>ti 才能做该任务,任务立刻完成,完成后 TT+bi;问是否可以完成每个任务

容易发现 bi0 的任务能做就做 考虑分类 发现是定序问题,考虑临项交换

考虑将所有任务按照 bi 是否 0 分类

  1. bi0,显然我们能做就做,将其按照 ti 从小到大排序依次做,做不了则无解
  2. bi<0,考虑 临项交换法
    • 设我们原本的完成任务序列为 i,i+1,则需要满足 T>tiT+bi>ti+1
    • 若交换 i,i+1 则需要满足 T>ti+1T+bi+1>ti
    • 由于此时 T 递减,若 TtiTti+1 则必然无解,换不换没有区别;其次,无论换不换,完成这两个任务后都有 TT+bi+bi+1,对之后没有影响,因此我们只需要使完成第二个任务的可能性最大
    • 综上,若交换则必然有 ti+1bi>tibi+1ti+bi<ti+1+bi+1,因此我们按照 bi+ti 从大到小排序最优

按照上述策略分类并排序后模拟即可

*P2431

题意为给定 A,B,需要从数列 20,21,22, 中选一些数,记这些数之和为 sum,要求满足 AsumB,问最多能选几个数

题目明示从二进制角度考虑 无法直接确定当前选择是否更优 考虑调整法 (实质是悔贪)

观察数列的形式,容易想到从 二进制 的角度考虑;大致考虑递减,而一次操作 消掉多个 1 是不优的,因此我们大概考虑如下算法

A,B 二进制分解,设 |B|=k,考虑从 2k1 开始递减尝试;对于 0pk,若当前数减去 2p<A 则不进行此操作(因为当前数单调不增,减去后以后就都不合法了);若当前数减去 2p<B 则结束尝试

考虑证明这么做的正确性,显然选择 2k1 作为起点一定能在消掉一些 1 后得到最终答案,一定是充分的;又因为在当前消 1 满足要求的前提下,容易发现消去高位上的一定必消去低位上的更优,同时消去多个 1 一定不会比当前更优(之后的操作相同,但这一步的代价更大),因此是正确的

AGC004D

题意为有 n 个点,每个点向另外一个点连边,保证任何一个点一定能走到 1 号点;现在希望修改一些点的出边使其指向另外一个点,最终使得任何一个点顺着边走 k 次后都在 1 号点

分析并转化题意 发现当前最优决策即为最优 (可以用调整法证) 直接贪心即可

先分析下为什么每个点走 k 次后都能在 1 号点;显然都在走 k 次后刚好第一次到达 1 号点是不可能的,那么只有可能是 走到 1 号点后开始来回绕圈子;如果到 1 号点后绕的环长度 2,那么所有点到 1 号点的距离都应当相同,即所有点都应当直接指向 1 号点,这显然是不优的;因此 1 号点必然指向自己

因此问题转化为修改其他点的出边使得其在 k 步(及)之内到达 1 号点;修改 1 号点后,此时所有点构成了一棵树(如果成环则一开始就走不到 1 号点),容易发现对于一棵子树上的点,贪心地 更改深度最小的点 总是最优(因为这样要改的总点数相应较少)

综上,每次我们找到 深度最大 的点,向上跳 k1 步后将当前点直接指向点 1 即可;实现时可以用线段树动态维护 dep

*CF525D

题意为给定一个只包含 .* 的矩阵,要求将最少的 * 改为 . 使得所有 . 的联通块都构成矩形(联通块可以不止一个),求最少更改数量

分析题目性质

结论:更改一格中的 *. 的充要条件是 在一个包含该格的 2×2 矩阵中有且仅有该格一个 *

首先,如果出现这种情况必然需要更改该格,有充分性;另外,若更改所有上述情况中的 * 后仍有残缺的 . 矩形,在该矩形的四角处必然会出现上述情况,矛盾,因此有正确性

实现时可以对于每个要改的 * 做 dfs,边改边搜即可,注意要搜八连通

*AGC007B

题意为给定 p1,p2,,pn,要求构造 a1,a2,,anb1,b2,,bn,使其满足

  • a1<a2<<an
  • b1>b2>>bn
  • ap1+bp1<ap2+bp2<<apn+bpn

从简单情况考虑 考虑如何推广

直接构造似乎并不好做,我们循序渐进,先考虑简单情况

首先考虑是否可以构造 ap1+bp1=ap2+bp2==apn+bpn,即 全都相等的情况,这么做的好处是我们可以暂时忽略 pi 带来的影响;容易发现在 ai=i,bi=ni+1 时可以做到这一点

现在我们尝试对每个 pi 所对的位置加上一些东西,使其满足题目限制;注意到 ai 序列与 api+bpi 序列的限制都是单调上升,可以考虑 固定 bi 不动,只在 ai 上加东西,不妨令 apiapi+(i1)

于是问题转化为使得每个 ai 在加上增量之后仍然满足整个序列单调上升;显然增量最多为 n1,一种方法是构造 ai=(i1)×n+1bi=n2n+2ai,之后令 apiapi+(i1) 即可

P.S. 这道题启示我们若直接做不好做,可以 先从弱化版考虑,逐渐推至正解

P2209

旅行家问题:你有一辆油箱容量为 G、开始有 B 单位油的车,要走 D 单位距离,走一单位距离耗一单位油。沿途有 N 个加油站,知道每个加油站离起点的距离 Xi 与该加油站每单位油的价格 Yi,问是否能到达目的地,若能则最小花费是多少

注意到当前最优决策即为最优

首先,显然需要将加油站按距离排序;为方便做题,我们可以将起点视作 Xi=0,Yi=+ 的加油站,将终点视作 Xi=D,Yi=0 的加油站

首先判断无解情况,显然是你加满了油也开不过去:

  • 当你在起点且下个加油站离你超过 B 单位距离
  • 当你不在起点且下个加油站离你超过 G 单位距离

其次考虑贪心,显然我们 总是往最便宜的走;因此首先找到能走到的(距离 G 的)所有加油站中最便宜的那个

  • 不是当前加油站,直接走过去即可,油不够就加到刚好能过去
  • 是当前加油站,先加满油,再找到剩下能走到的加油站中最便宜的并走过去

直接模拟即可

贪心整合包 (medium)

诶你怎么全打星啊

*P2751

N 个工件、M1 个 A 型机器、M2 个 B 型机器,每台机器平行且独立工作,一次只能干一个工件;A 型机器从输入库接受工件,花费 T1i 时间加工后将其放置在缓冲库;B 型机器从缓冲库接受工件,花费 T2i 时间加工后将其放置在输出库;询问:

  • 使所有工件都受过 A 型机器加工的最晚时间最小值
  • 使所有工件都受过 B 型机器加工的最晚时间最小值

第一问好做,当前最优决策即为最优 考虑套用结论解决第二问 发现不对,考虑错因 从简单版本考虑问题 进行推广

对于第一问,每个工件到达时间相同,能越早加工就越早加工,肯定总是贪心地选择 最早的空闲 A 型机器;因此可以使用小根堆维护所有 A 型机器的最早空闲时间点,每次取出队头 T1i 并将一个零件在 T1i 时刻加工,再将 T1i+T1i 入队(因为该机器的下个最早空闲时间点为 T1i+T1i

对于第二问,每个工件到达时间不同,第一问的贪心策略不再适用(例如我们可以先用次优的 B 型机器加工先到的工件,再用最优的机器加工后到的工件)

还是 先从简单版本考虑,如果只有一台 B 型机器,完成时间有何规律?不妨设 ti 为第 i 个工件的到达时间,fi 为第 i 个工件的完成时间,容易发现递推关系 fi=max(fi1,ti)+T2

输出 fi 的递推过程,能够看出一定是 max(ti+(ni)T2),因为一定是在一个地方的 ti 取到 max,之后一直加 T2;推广到所有 B 型机器各自的加工序列上,这个规律一定也是满足的

因此,我们仍然可以采用第一问的方法得到前 N 个 B 型机器最早空闲时间点,现在问题转化为 ti 与这 N 个最早时间点匹配使得两两加起来的最大值最小;这也是一个经典的贪心问题,这里直接给出结论,我们将 ti 从小到大排序、时间点从大到小排序,按下标一一对应即可

*P3076

题意为在一个长度为 m 的数轴上,有 n 头牛要从 si 前往 ti;有一辆出租车从最左端 0 处出发,要将所有牛都送到目的地,最后到达最右端 m,出租车一次只能载一头牛且可以把牛半路扔下去,问最小行驶距离

考虑拆贡献 分析过程,推导性质 考虑过程带来的影响

首先,显然我们一定要走过 |siti|,必须把每个牛从其起点送至目的地;出租车来回接客还会花费一些额外距离,现在我们希望使接客走的距离最小

考虑接客的过程;直接从上一个牛的终点匹配最近的起点是不对的,因为我们可能会半路扔下当前牛,先去送另一头牛再回来接该头牛;显然我们 只会在有牛的地方扔下牛

为方便叙述,不妨设扔下的牛为 i,新接的牛为 j;此时两头牛处于同样的位置,可以视为牛 i 代替牛 j 留在起点 sj,牛 j 接替牛 i 前往 最近的终点,此时拉客的路程就是送完牛 j 后去某起点接另一头牛的路程;可以发现扔牛的操作解除了 siti,sjtj 的对应关系,拉客的路程即为 将任意起点与任意终点两两匹配 后距离和的最小值,即 |sitj|

这同样也是一个较为经典的贪心问题,同样直接给出结论:将 si,ti 均从小到大排序后按下标一一对应即可;证明的话,可以画个数轴分讨 ti,tj(titj)si,sj(sisj) 的位置关系,容易直观看出交换一定不优

P5521

题意为给定一棵 n 个节点的树,1 号点为根,每个点有点权 wi;现从根出发遍历这棵树,当你在节点 i 时可以选择走到 i 的一个没到过的儿子或 i 的父亲,同时规定能够在点 i 放置梅花当且仅当点 i 的任意儿子 j 上都放了 wj 朵梅花;你在任意时刻可以回收任意节点的梅花(不必走到该节点),对于每个节点 i,询问在 i 上放 wi 朵梅花所需的最小总梅花数

发现是对儿子的定序问题 临项交换

考虑设 fi 表示点 i 的答案,对于点 u,考虑 临项交换法;设之前遍历过的儿子所需最小总梅花数为 sum,当前遍历儿子 ai,下一个遍历儿子 ai+1,则易知不交换的答案为 max(sum+wi+fi+1,sum+fi),交换的答案为 max(sum+wi+1+fi,sum+fi+1)

因此,我们需要比较 max(wi+fi+1,fi)max(wi+1+fi,fi+1);考虑分类讨论

  1. 左式在 fi 取 max,则 fifi+wi+1max(wi+1+fi,fi+1),不需要交换,成立
  2. 左式在 wi+fi+1 取 max,则需保证 wi+fi+1wi+1+fi,即 wifiwi+1fi+1

综上,按照 wifi 不降排序后直接模拟即可

*CF549G

题意为给定 n 个数 a1,a2,,an,可以进行任意次以下操作:

  • ai1>ai,则令 ai1ai11,aiai+1,最后交换 ai1ai

若最终能够使整个序列非降,则输出最终序列;否则输出 :(

发现与排序有关 分析题目性质,寻找不变量

注意到序列非降与排序有关,考虑寻找操作中的 不变量;如果 ai 向左换,有 ii1,aiai+1,向右换则有 ii+1,aiai1,动用你敏锐的注意力可以发现一定有 ai+i 保持不变

此时做法就非常显然了,将 ai+i 从小到大排序得到 ai,最终序列即为 aii;如果最终序列不满足非降则无解

P.S. 这道题启示我们发掘变化中的不变量可能有奇效

*CF798D

题意为给定两个长度为 n 的序列 ai,bi,选择 n2+1 个下标组成集合 S 使得:

  • 2×iSai>ai
  • 2×iSbi>bi

消去 +1 转化题意,考虑分组

n2+1 中的 +1 一看就很烦,我们先将 ai 排序得到 ai 后选择 a1

注意到题目的要求实际上等价于选出的数之和大于其余数之和;考虑 两两分组,令 a2a3 为一组、a4a5 为一组……,每组中选出对应 bi 较大的那个

由于我们两两分组且每组中都选了 bi 较大的,选出的数一定满足题目限制;同时对于 ai,最坏情况即为选择 a1,a3,a5,,又因为 a1>a2,a3>a4,,一定也满足题目限制,因此这么构造一定合法

P.S. 这道题启示我们遇到 ×2÷2 时考虑两两分组分析

**P7831

好题!

题意为给定一个 n 个点 m 条边的有向图,一个旅行商在这些点之间旅行;每条边有限制 ri 与报酬 pi,只有旅行商当前的资产 ri 才可以通过该边,通过后旅行商的资产会增加 pi

分析题目性质 发现 dp 式子 考虑为什么不能转移,进而考虑消环 拓扑排序 考虑对于环如何更新答案 发现应从最大边入手,这契合我们的观察

现要求旅行商可以永远不停地走下去,对于每个点,求从该点出发满足上述要求时旅行商至少要有多少初始资产

首先考虑 调整法,先将初始资产 ansi 设为 +,之后逐渐下调

观察题目性质,注意到:

  • ansimaxri 时可以在整个图上畅通无阻,因为报酬 0;同时这启示我们 从大到小考虑边
  • 不妨设 dpu 为点 u 的答案,显然可得转移柿子 dpu=min(dpu,max(r(u,v),dpvp(u,v)));但是不能直接 dp,因为原图中可能有环

既然有环导致不能转移,我们考虑 消环,使用 拓扑排序 的思想

  • Key Observation:走向原图中出度为 0 的点没有意义

类似拓扑排序,我们开一个队列 q 记录出度为 0 的点;考虑按 ri 从大到小遍历反图中边 ei

  • q 非空,取出队首 v,遍历反图中的边 (v,u),若 (v,u) 未被删且 ansv+,则按照上述 dp 转移柿子用 ansv 更新 ansu;由于 (v,u) 这条边的意义只在于可以通过 v 更新 u,现在它的使命已经完成了,可以将边 (v,u) 删去,若此时 u 的出度为 0 则将其入队
  • 若遍历完 q 后边 ei(vi,ui,ri,si) 仍未被删除,则说明从 ui 只能走到出度非 0 点,即 走到一个环;由于我们从大到小考虑边,走到的环中的 maxrj 一定 ri,因此此时只需要初始资产为 ri 即可畅通无阻,ri 更新 ansui;最终删去 ei,若 ui 出度为 0 则将其入队

类似拓扑排序,每个点只会入队一次,时间复杂度 O(mlogm),瓶颈在于排序

P.S. 这道题启示我们将不同的算法思想结合

P8113

题意为给定 n 个数 a1,a2,,an,需要将这 n 个数重排得到 a1,a2,,an,初始时 sum=0,从小到大遍历 ai

  • sumi1>ai 则令 sumsum+0
  • 反之,则令 sumsum+m

求最终所有可能得到的 sumn 的 max 与 min

注意到这是定序问题 临项交换

观察样例,大概可以猜测取最大值时将序列 从小到大排序,取最小值时将序列 从大到小排序

记经过前 i1 个数后和为 sum,现希望取最大值,考虑使用 临项交换法 证明:

  1. sumi1aiai+1

    • 不交换时,ai 处贡献为 mai+1 处贡献有可能为 m
    • 交换时,ai+1 处贡献为 mai 处贡献有可能为 m

    又由于 aiai+1,不交换时贡献为 m 的可能性最大;如果不交换时贡献 m,交换后贡献 0,确实可能会导致后面某处不交换时贡献 0,交换后贡献 m,但 再往后 sum 相同,因此 不会更劣

  2. aisumi1ai+1

    • 不交换时,ai 处贡献为 0ai+1 处贡献为 m
    • 交换时,ai+1 处贡献为 mai 处贡献为 0

    此时无论换不换对 sum 的总贡献都是 0+m=m

  3. aiai+1sumi1

    • 不交换时,ai 处贡献为 0ai+1 处贡献可能为 m
    • 交换时,ai+1 处贡献为 0ai 处贡献可能为 m

    又由于 aiai+1,不交换时贡献为 m 的可能性最大;至于是否会导致后面更劣与第一种情况类似,这里不再赘述

综上,从小到大排序最优;取最小值时证明类似

**P3045

题意为有 n 头奶牛,每头奶牛的价格为 pi,有 k 张优惠券,使用优惠券购买第 i 头奶牛的价格为 ci(cipi);一头奶牛只能用一次优惠券,问用不超过 m 的钱最多能买多少头奶牛

分析朴素贪心错因 发现无法确定当前最优决策是否总最优 考虑悔贪 希望使变量最小,因此对优惠券而非牛反悔 整理策略,比较相似处,进而优化写法

容易想到一个简单贪心:将所有优惠价格与普通价格排序,之前没买过该牛就选上;但是自己造几组数据就能发现这个贪心是假的,例如:

3 2 6
2 1
3 1
5 2

为什么假了?牛二的优惠券价格相比牛三更优,但实际上选择牛二就买不了牛三;相比之下牛三省钱更多,省下来的钱正好可以再买牛二

这启示我们进行 反悔贪心;注意到如果对买的牛反悔,带来的变量太多,价格、所买牛数都会变化,过程较为复杂,因此不妨选择 对用过的优惠券反悔;这意味如果之前在一头牛身上用优惠券,对其反悔时只转移其身上的优惠券,仍然用原价购买该牛,因此 买过的牛只增不减

考虑对于两头牛 i,j,将 i 身上的优惠券转移至 j 的条件:

  • 不转移,总价格为 ci+pj
  • 转移,总价格为 pi+cj

综上,转移时要求 ci+pjpi+cj,即 pjcjpici;因此我们可以开一个 小根堆,记录当前选择用优惠券的所有牛的 pici

由于我们不考虑转移优惠券,选择用优惠券一定是全场最优;因此,我们应该将 当前转移优惠券最便宜 (每次取优惠价最便宜的牛)与 当前原价最便宜 (每次取原价最便宜的牛)进行比较,总结一下贪心策略:

  1. 按照 ci,pi 分别扔进小根堆 qc,qp,同时开小根堆 qs 记录买过的牛的 pici
  2. 每次取出 topqc,topqp,topqs 进行比较,则优惠的差价为 topqc+topqs
    • topqptopqc,直接买 topqp
    • topqp>topqc,此时如果有优惠券则直接买 topqc,若没有优惠券:
      • topqptopqc+topqs 则买 topqp
      • 反之,转移优惠券

事实上我们还可以继续改进贪心策略;注意到比较 topqptopqc 的过程可以与比较 topqptopqc+topqs 合并,这相当于 topqs=0;因此我们可以 先设所有 topqs=0,一开始没有用优惠券时相当于比较 topqptopqc

总结一下改进后的贪心策略:

  1. 按照 ci,pi 分别扔进小根堆 qc,qp,同时开小根堆 qs 记录买过的牛 pici
  2. 每次取出 topqc,topqp,topqs 进行比较:
    • topqptopqc+topqs 则买 topqp
    • 反之,转移优惠券

P.S. 这道题启示我们固定状态以简化转移策略,同时可以比较转移策略中相似之处以寻找优化方式

*P1484

题意为有 n 个数 a1,a2,,an(可能为负),可以选择至多 k 个两两不相邻的位置组成集合 S,求 iSai 的 max 值

分析朴素贪心错因 发现无法确定当前最优决策总最优 考虑悔贪 设计为相同问题,实现自动反悔

直接从大到小贪的问题是可能选到相邻的;注意到对于位置 i,如果 不选 ai 则必选 ai1ai+1(不然还不如只选 ai ),因此考虑在选择 ai 后删掉 ai1ai+1 并将 ai1+ai+1ai 加入队列以模拟反悔操作,此时每次选最大的就是最优的

为实现上述操作,可以考虑采用 模拟链表 的方式实现

*P2893

题意为给定 n 个数 a1,a2,,an,将 ai 变为 bi 的代价为 |aibi|,希望将原序列变为一个不降或不升序列,求最小代价

发现不好直接刻画变化结果 分析变化过程 推导性质,基于性质考虑下一步做法

考虑分析变化的性质;不妨设 ai1 变为了 bi1,对于 ai,有如下选择:

  • aibi1,此时无须改动,bi=ai
  • ai<bi1,此时可以选择:
    • ai 上调至 bi1,即令 bi=bi1
    • ai 上调一部分(可以不上调),再将 bi1bk 下调(因为下调 bi1 会连带着导致之前一部分也下调);此时问题变为选择一个数 x 使得 |aix|+|bi1x|++|bkx| 取 min,易知 xai,bi1,,bk 的中位数

综上,可以发现 ai 要么不变,要么变为之前的一个 bi;而又必然有 b1=a1,因此可归纳得到 每个 bi 都对应原数组中的某个 ai

有了这个结论,我们考虑 dp;以非降情况为例,设 dp[i][j] 表示对于前 i 个数,令 bi=aj 时的最小代价,转移方程即为 dp[i][j]=mindp[i1][k](ajak)+|aiaj|

直接做是 O(n3) 的,将 ai 从小到大排序后每次转移过来的区间是一段前缀,可以边转移边做 前缀 min 优化,时间复杂度 O(n2)

Bonus:如果需变为严格递增序列,可以在一开始令 aiaii 后转化为变成非降序列,最终把 i 加回来即可

Bonus:如果只需变为非降序列且要求时间复杂度 O(nlogn),可以考虑继续深入分析变化的性质;对于当前数 ai,设之前最大值为 v,我们总需要选择 [ai,v] 间的一个值 k 并令 aik,vk,代价总是 vai

此时由于选择的 k 受限(我们需要保证改变后之前的序列仍满足非降)暂时视为 ai,v 均改为次大值(如果次大值 ai 则视为改为 ai );实际上,如果对 k 没有限制,显然将 ai,v 均改为 ai 是最优的(当前越小对后面可能的影响就越小),因此我们 ai 加入大根堆,代表的意义是有朝一日 ai 处于堆顶时其前面所有大于它的数都一定已经被改到小于它,此时对于之前 ai,v 的改动就可以直接视为 aiai,vai

实现时只需要维护大根堆,时间复杂度 O(nlogn)

P.S. 这种做法的精髓是保留更改的不确定性,只把贡献先累加

*P3620

题意为给定 n 个位置 a1,a2,,an,你需要选择 k 对位置,每对位置的代价为 |aiaj|,一个位置最多被选一次,求最小的代价和

稍作分析可以得到不可能同时选择形如 ai1ai2aj1aj2(即相交)或者 ai1ai2aj2aj1(即完全包含)这样的 (ai1,aj1)(ai2,aj2) 位置对,因为我们可以按照大小顺序令最小与次小一组、最大与次大一组,这样一定是更优的;又因为位置对的跨度越小越好,因此我们 总是选择相邻的位置组成位置对

将原数组差分,转化为 选择 k 个不相邻元素使总和最小;这个问题几乎等价于 P1484,但直接贺上来会获得 45pts 的好成绩,因为如果我们选择 头或尾元素,之后选择其反悔点时总点数没有增加,不满足必须选 k 个的要求,因此特判一下即可

*CF865D

题意为有一个商品,第 i 天的价格为 pi,每天可以选择买入一个、卖出一个或什么都不干,开始时你手里没有商品,你希望第 n 天结束后手上也没有商品,最大化盈利

不好判断当前最优决策是否总最优 考虑悔贪 设计反悔策略,实现自动反悔

由于最后你手上没有商品,每一个买入的商品最终都会对应在某天卖出;不妨考虑第 i 天买了一个商品并在第 j 天卖出,获利为 ajai,但是可能在第 j 天后面的第 k 天卖出 获利更多,因此需要进行 反悔贪心

考虑 引进新的物品 刻画反悔操作,即需要创造一个价格为 v 的物品,使得“第 i 天买入第 j 天卖出”同时“第 i 天买入 vk 天卖出”相当于“第 i 天买入第 k 天卖出”;综上,有:

  • ajai+akval=akai,即 aj=val

注意到这个式子与“第 j 天买入第 k 天卖出”形式完全相同,因此问题变得非常方便,考虑使用 小根堆 维护可买的低价商品与反悔商品,在每第 i 天:

  • 考察是否能够购买堆顶 (即 ai>top ),可以则购买并将 ai 插入堆中
  • ai 插入堆中(注意与上一步是独立的)

**P3162

题意为有 n 种零件,给定数轴上 m 个生产车间的位置 xi 与生产的零件编号 pi;现需要在数轴上某一位置 x 修建一个加工车间,每个零件编号 i 对其的代价为 min(|xxi|2)(pi=i),希望使得代价和最小,求修建位置

从简单情况入手 考虑如何推广 考虑如何优化朴素过程 发现仅需包含最优解,推导包含最优解的性质

首先考虑如果已经知道离 x 最近的 n 个车间为 x1,x2,,xn,如何选定 x 的位置;即我们希望使 (xxi)2 最小,易得:

(xxi)2=nx22xxi+xi2=xi2nx(2nxix)xi2n(x+2nxix2)2=xi2(1nxi)2

因此,我们有:(xxi)2xi21n(xi)2,在 x=1nxi 时取等

直接枚举可得 O(mn) 做法;枚举太不优了,考虑如何优化选择过程

对于每个零件编号 i,我们将所有 pi=i 对应的 xi 从小到大排序,首先全部选择 xi,1;之后的每次选择,我们钦定一个已选位置,将其替换为其 在排序后数组中的下一个位置;这么换显然会漏过一些情况,我们考察必然能覆盖到最优情况的条件

考虑反面,不妨设存在先后两次替换为 xi,1xi,2yi,1yi,2,则覆盖不到等价于 最优情况为 xi,1yi,2;不妨设 xi2(xixi,1)=p,再设 xi(xixi,1)=q ;则将 xi,1 替换为 xi,2 带来的变化为:

[(p+xi,12)1n(q+xi,1)2][[(p+xi,22)1n(q+xi,2)2]=(xi,12xi,22)+1n(2q(xi,2xi,1)+xi,22xi,12)=1n(xi,1xi,2)((n1)xi,1+(n1)xi,22q)

不妨设最优时 s=xi(即取 xi,1yi,2 时),次优时 s=xi;又因为 xi,1<xi,2,则只需看后一项符号:

(n1)xi,1+(n1)xi,22q=n(xi,1+xi,2)(q+xi,1)(q+xi,2)=n(xi,1+xi,2)ss>0xi,1+xi,2>s+s

同理可得 yi,1+yi,2<s+s

综上,我们有 xi,1+xi,2>s+s>yi,1+yi,2;因此将 xi,1+xi,2 从小到大排序后可以保证一定覆盖到最优情况

实现时直接模拟即可

**P1248

题意为需要加工 n 个产品,每个产品要先经过 A 车间加工再去 B 车间加工,第 i 个产品在 A 车间加工时间为 ai,在 B 车间加工时间为 bi,求总加工时间最小值

注意到是定序问题 临项交换 不好直接推,不妨从简单情况考虑 发现不满足不可比性的传递性 分析决策性质,考虑从极端情况入手

首先 从简单情况入手,考虑只有两个产品的情况;此时:

  • 先加工产品 1 再加工产品 2,代价为 max(a1+b1,a1+a2)+b2=a1+b2+max(b1,a2)
  • 交换,代价为 max(a2+b2,a2+a1)+b1=a2+b1+max(b2,a1)

注意到 max(a,b)=a+bmin(a,b),将两式作差,即 比较 min(a1,b2)min(a2,b1)

此时一个显然思路是按照上述策略直接排序;很不幸这是错的,因为原式在 临项取等 时可能出现问题

  • 比如 a1=2,a2=1,a3=3,b1=4,b2=1,b3=5,其虽然满足 min(a1,b2)min(a2,b1)min(a2,b3)min(a3,b2),但是不满足 min(a1,b3)min(a3,b1)

这个式子虽然不支持直接排序,但从极端情况入手能发现一些比较好的性质

Key Observation1:该不等关系下,若 (ai,bi)ai 取全局 min 则任何其他 (aj,bj) 都比它大

同理,若考虑 bi,也有相似性质

Key Observation2:该不等关系下,若 (ai,bi)bi 取全局 min 则任何其他 (aj,bj) 都比它小

这启示我们将 aibi 放在一起每次取出全局最小值,由以上两结论可以 确定加工顺序;具体的,维护两个指针 l,r,开始时令 l=0,r=n+1

  • 若最小值在 ai 取到,则 ll+1,在第 l 个加工 i
  • 若最小值在 bi 取到,则 rr1,在第 r 个加工 i

最终按照确定好的加工顺序模拟即可

P.S. 这道题启示我们临项交换法有时需要特殊考虑取等(好像叫不可比性的传递性),同时有时从极端情况入手可以发现有用性质

*CF436E

题意为有 n 个关卡,对第 i 个关卡可以花 ai 的代价获得 1 颗星或花 bi 的代价获得 2 颗星或不玩,求获得 w 颗星所需的代价最小值

无法确定当前最优决策是否总最优 考虑悔贪 推导变化过程,设计策略使得能够自动反悔

可以很明显的看出是悔贪,不过这道题稍有区别;普通悔贪一般只有两个选择(反悔与否),这道题的选择数量相对较多,具体的,我们考虑 增加一颗星的方式

  • 选择一个没玩过的关卡 i,该关卡选择 ai
  • 选择一个选 ai 的关卡 i,该关卡改选 bi
  • 选择一个选 ai 的关卡 i,该关卡不选,另在一个没玩过的关卡 j 选择 bj
  • 选择一个选 bi 的关卡 i,该关卡改选 ai,另在一个没玩过的关卡 j 选择 bj

综上,我们需要维护:

  • ai 的小根堆,对应第一种方式
  • biai 的小根堆,对应第二种方式
  • ai 的大根堆与 bi 的小根堆,对应第三种方式
  • aibi 的小根堆与 bi 的小根堆,对应第四种方式

实现起来细节较多,需要仔细考虑增删关系

*CF1601D

题意为有 n 个人去登山,每个人有属性 si,ai,山的初始高度为 d,对于每个人:

  1. dsi,则他登山成功,令 dmax(d,ai)
  2. 反之,他登山失败,无事发生

注意到是定序问题 临项交换 发现 ai,si 的大小关系会对 d 产生影响 考虑分类,两类内部的贪心策略是显然的 考虑两类间的临项交换,大力分讨即可 注意特殊情况

现在需要为这 n 个人决定登山顺序使得登山成功的人最多,求登山成功的人数的 max

注意到 aisi 的大小关系会对登山成功后山的高度产生影响,考虑按是否 aisi 分类:

  1. aisi,对于一对 (ai,si),登山成功时有 dmax(d,ai)si,我们希望山的高度最小,因此 si 从小到大排序
  2. ai>si,对于一对 (ai,si),登山成功时有 dmax(d,ai)=ai,同理 ai 从小到大排序

接下来考虑两类情况之间的影响,不妨设有 aisiaj>sj,大力分讨其大小关系带来的影响:

  1. sj<ajaisi,选 j 先更优
  2. sjaiajsi,选 j 先更优
  3. sjaisiaj,此时只能选一个,先选 jdaj,先选 idmax(d,ai)si,显然选 i 先更优
  4. aisisj<aj,选 i 先更优
  5. aisjsiaj,选 i 先更优
  6. aisj<ajsi,此时选 j 先必然能都取到,选 j 先更优

观察选取的性质,可以发现每组中的最大值都不取;因此考虑 按照 max(ai,si) 从小到大排序

为什么能多登就多登一定更优?在这么排序的前提下,如果后面的 max(aj,sj)sj 处取 max 则仍然不影响其登山;若在 aj 处取 max,则就算前面少登一次换来 j 处多登一次,总次数也仍相同,同时山的高度反倒会增加,一定不优;因此能多等就多登一定最优

需要特别注意,此时取等不意味着可以随便排;取等时,情况 1、2、6 仍要求先选 j情况 3、5 此时先选 j 可以同时取到 i 因此先选 j 更优,观察选取性质可发现此时必然选取 min(si,sj);因此考虑 按照 si 从小到大排序

综上,我们按照 max(ai,si) 为第一关键字排序,si 为第二关键字排序,直接模拟即可

*P4785

题意为给定 1n 的一个排列 ai,你需要进行连续的 n1 轮操作,编号分别为 2,3,,n;在编号为 i 轮操作你可以交换 ai,ai2 或什么都不做,输出最终可能得到的所有序列中字典序最小的

注意到题目表述贴近二叉树,转到树上考虑 分析选择策略,发现难点只在于选 ai×2+1ai,ai×2 的顺序 首先考虑模拟,注意到时间复杂度可以接受,直接记忆化即可

注意到 i,i2 很贴近 二叉树 的表示,不妨在以 1 为根、i 的儿子为 i×2i×2+1 的二叉树上考虑问题

对于 ai,ai×2,ai×2+1,考虑分情况讨论其大小关系:

  1. ai 最小,显然什么都不做最优
  2. ai×2 最小,显然只交换 ai,ai×2 最优
  3. ai×2+1 最小,此时 i 号点上的值需要为 ai×2+1,但 i 的两个儿子的值可以随意调换;儿子怎么放最优,取决于 aiai×2 最终会被换到哪里去

情况 3 似乎不太好直接看出来;没关系,注意到 可能从点 i 上向下交换的值只能来自其祖先,而原树类似于完全二叉树,树高期望在 O(logn) 级别;因此考虑直接 记忆化模拟

具体的,记 fv,p 表示将值 v 从点 p 向下交换最终到达的位置;对于点 i 的情况 3,我们记 v=min(ai,ai×2),再令 pl=fv,i×2,pr=fv,i×2+1

  1. pl<pr,则我们将 v 换到 i×2
  2. 反之,我们将 v 换到 i×2+1

为什么?因为无论我们怎么换,对于 1min(pl,pr)1 上的数都不会有影响(交换只关注大小关系,而 ai,ai×2 都比其大,本质上没有区别,其该怎么换就怎么换),而 min(pl,pr) 上的数显然越小越好

对于 fv,p,按照相同的策略求解即可

时间复杂度 O(n2logn)

*CF351E

题意为给定长为 n 的序列 ai,可以任意改变任一个数的符号,求最小逆序对数

分析变号操作带来的影响 推导题目结论 依照结论设计策略

由于可以任改符号,初始时可令 ai|ai|

Key Observation:对于 ai>aj

  • ai 取正,无论 aj 怎么取都有 ai>aj
  • ai 取负,无论 aj 怎么取都有 ai<aj

这启示我们 从大到小 考虑 ai;不妨设当前最大值为 ak,若其取正,则贡献为其右边所有数的数量,取负则为其左边所有数的数量;由于其他数与 ak 的贡献在当前轮就会被考虑完,因此可以视为 ak 怎么取 对其他数没有影响,我们直接取两种情况的最小值,最后删去 ak 即可

直接模拟,时间复杂度 O(n2)

Bonus:可以发现对于第 i 个数,计算其贡献时挖去了所有比它大的数;因此记 lhs=1j<i[aj<ai],再记 rhs=i<jn[aj<ai],贡献即为 min(lhs,rhs);使用权值树状数组或权值线段树容易做到 O(nlogn)

*ARC073C

题意为有 n 个包,每个包中有两个编号分别为 ai,bi 的球,你需要将每个包中的球一个染红一个染蓝;记染红的球中最大编号为 rmax、最小编号为 rmin,染蓝的球中最大编号为 bmax、最小编号为 bmin,要求最小化 (rmaxrmin)(bmaxbmin),输出该值

不妨给一定出现在这四个值中的值定色 分类讨论,推导策略

不妨设 ai>bi;首先考虑将全局 max 编号染为红色,分类讨论全局 min 编号的染法:

  • 将全局 min 编号染为蓝色,显然对于每个包将 ai 染红、bi 染蓝最优

  • 将全局 min 编号染为红色,此时不妨考虑若 确定 bmin 该如何染色:

    • 对于 biai<bmin,不成立
    • 对于 bibminai,只能将 ai 染蓝
    • 对于 bminbiai, 将 bi 染蓝最优

    观察上述策略,容易发现实际上是根据 bibmin 的大小关系 分类;考虑按照 bi 从小到大排序,则答案必然形如将 前一段的 ai 染蓝a1 除外)、后一段的 bi 染蓝bn 除外),直接扫一遍枚举分界点维护即可

**AGC034C

给定非负整数序列 li,ri,bi 与上界 X,求最小的 s,使得存在非负整数序列 ai,ci,满足:

  • aiX
  • i=1nai=s
  • ci[li,ri]
  • i=1nci(aibi)0

简化题意,消去变量 尝试调整分析性质 首先考虑暴力,发现时间复杂度可以接受,于是直接枚举即可

题目看上去有点复杂,考虑如何简化;不妨设 fi(x) 为第 i 项在 ai=x 时的贡献,易知:

  • x<bi 时,应令 ci=li,有 fi(x)=li(xbi)
  • xbi 时,应令 ci=ri,有 fi(x)=ri(xbi)

由于 ri>li,当 x 增大系数 li/ri 也总增大,因此我们有:

  • Key Observation:当同时存在 0<xi<X0<xj<X,将系数较大者 +1,较小者 1 总会更优

由这一关键性质,我们可以得到所有 xi一堆 0、一堆 X 和至多一个非 0X 数组成;更进一步,这个非 0X 的数必然是 smodX,且我们必然会填 sXX

注意到 xi 的组成与 s 有关,又由于显然 s 越大能够填的 xi 就越大,原式的结果也会更大,因此考虑二分 s 转化为判定问题

考虑初始令所有 xi=0,算出 xi0 变为 X 的变化量,贪心地将 变化量最大 的几个 xi 变为 X 肯定最优;对于特殊的一个 xk=sX 可以直接枚举 k 的位置

实现时可以用前缀和维护变化量,时间复杂度 O(nlog2n)

ARC147E

题意为有 n 个数 a1,a2,,an,可以将其任意两两交换,给定 b1,b2,,bn,要求最终满足 aibi 且需要最大化未被交换的 ai 的次数;输出该次数,无解输出 1

题目明示需要分类 最小化变劣的影响

考虑分为 aibiai<bi 两类,即代表开始时不需要交换与需要交换的两类;对于 ai<bi 中的元素,由于我们希望尽可能少交换,因此能用这里面的 aibi 自给自足是最优的;容易发现将该类 maxbimaxai 匹配最优

但是如果 maxbi>maxai,说明此时必须从 aibi 一类中引进新元素;显然与 maxbi 交换的 (ai,bi) 必须满足 aimaxbi,又由于希望尽可能少交换,我们贪心地希望 bi 越小越好

注意 (ai,bi) 交换后可能不再满足要求,所以需要将其 归到 ai<bi 类中

实现时可以考虑:

  • ai<bi 类开一个 bi 的大根堆
  • ai<bi 类开一个 ai 的大根堆
  • aibi 类开一个整体关于 ai 的大根堆
  • 对满足 ai>bi(ai,bi) 单独开一个关于 bi 的小根堆

**P7078

题意为有 n 条蛇,初始时每条蛇的体力值为 ai,编号为 x 的蛇比编号为 y 的强当且仅当 ax>ay,或 ax=ayx>y;接下来会进行若干轮如下操作:

  1. 实力最强的蛇 x 选择吃掉实力最弱的蛇 yaxaxayy 退出接下来的操作,之后立即进行下一轮操作
  2. 实力最强的蛇 x 选择不吃掉实力最弱的蛇 y,操作立即结束

每条蛇都希望在自己不被吃的前提下,吃掉尽可能多的蛇(它不会吃掉自己);假设每条蛇都足够聪明,求最终会剩下几条蛇

模拟题目过程推导性质 上 trick 维护

Key Observation:在最强蛇 x 吃掉最弱蛇 y 形成的 axay 不是新的最弱蛇的前提下,其必然递减

由这一性质,考虑对每轮操作进行分类讨论:

  1. axay 不为下一轮最弱蛇,则蛇 x 必然选择吃掉蛇 y;这是因为若之后的最强蛇 x 选择吃掉最弱蛇 y,必然有 axay<axay,蛇 x 必然嘎在蛇 x 之前,而蛇 x 既然选择吃就代表它不会嘎,那么蛇 x 也不会嘎,不吃白不吃
  2. axay 为下一轮最弱蛇,则需要考察下一轮最强蛇 x 会不会选择吃,这是一个 相同的问题;不妨设都选择吃的情况下递归 k 轮后出现情况 1 或只剩两条蛇,此时第 k1 轮的最强蛇不会吃、第 k2 轮会吃……对于 xk 为偶数则会吃,为奇数则不会吃;这种情况下操作必然在当前轮或下一轮结束

综上,我们边模拟边记录当前是否出现过情况 2、离第一次出现情况 2 递归了多少轮即可;不过直接做是 O(Tnlogn) 的,不足以通过此题

注意到一开始的关键性质,这启示我们原蛇和形成的新蛇都具有 单调性(进入情况 2 后每次只会吃新蛇的最小值,在出现非最小情况后会立即停止,实际上不影响);考虑使用两个 单调队列 q1,q2 维护原蛇和新蛇

每次的最大蛇即为 max(topq1,topq2),最小蛇为 min(backq1,backq2),形成的新蛇直接扔到 q2 的队尾即可

时间复杂度 O(Tn)

策略总结

对于邻项交换法

  • 常应用于定序问题
  • 直接推式子过于复杂时可以考虑从简单情况入手

对于反悔贪心

  • 常应用于无法确定当前最优决策是否总最优时
  • 考虑将原问题刻画为许多相同操作,通过局部最优性保证从当前情况的最优解到下一情况的最优解变化不会很大
  • 有时需要确定 "不变量",一般稳定变化,直到达到目标情况 (如目标为选 k 个数,"不变量"可能为每次反悔后稳定增加 1 个数)

P.S. 反悔贪心的正确性:

证明存疑。笔者想的也不大清楚

  • 考虑反证法,每一步进行归纳,只需要证明当前反悔决策在下一步最优解中即可
  • 如果反悔决策不在最优解内:
    • 最优解中必定有一个其他决策
    • 你在设计反悔策略时保证了反悔策略涵盖所有从的当前步推到下一步的决策
    • 由于反悔决策每次按照局部最优执行,其相对当前步的任意其他决策都更优;因此我们考虑将最优解中那个其他决策替换为当前反悔决策,必定更优
    • 你考虑将那个其他决策设置为与当前反悔决策冲突的决策,保证可以选择当前反悔决策

一些分析方法

  1. 首先考虑暴力的时间复杂度是否可以接受,如果可以则直接模拟
  2. 分析暴力操作过程 / 分析朴素贪心的错因,进而发掘题目性质,推导贪心策略
  3. 从简单情况入手后推广 / 从极端情况入手分析性质 有时很有用
posted @   lzlqwq  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示