BISHI49 小红闯关¶
中等通过率 21.47%python3样例通过牛客 AC贪心堆
一句话
每通过 k 关得一个跳关道具,用道具跳过的关不花时间。
解题思路¶
这题考什么¶
先把「什么时候能跳」写成约束。跳关也算「通过一关」,所以走到第 i 关之前, 已经通过了 i-1 关,手上累计获得 floor((i-1)/k) 个道具。因此
目标是最大化「被跳过的关的时间之和」,答案 = 总时间 - 该最大值。
这是经典的「前缀容量约束下选最大权子集」(拟阵上的贪心), 用小根堆在线维护: 从左往右扫 i,把 a_i 放进堆;容量 c_i = floor((i-1)/k) 是非减的, 若堆的大小超过 c_i,就弹出堆里最小的那个。 扫完后堆里剩下的就是最终要跳的关。
- 合法性:任一前缀 i 里被最终选中的元素,在第 i 步时都还在堆内, 而那时堆大小 <= c_i,满足 (*);
- 最优性:容量非减 + 每次只淘汰当前最小者,是标准的交换论证。
验算样例 2(n=6, k=2, a=[1,1,4,5,1,4]):c = [0,0,1,1,2,2]。 i=1 推 1 又弹掉;i=2 同理;i=3 堆={4};i=4 推 5 超容量弹 4,堆={5}; i=5 堆={1,5};i=6 推 4 超容量弹 1,堆={4,5}。跳掉 9,总时间 16-9=7 ✓。
数据规模与复杂度¶
n,k <= 1e5,a_i <= 1e5。堆操作 O(n log n),总和上界 1e10 需 long long(C++)。
坑在哪¶
- 跳关本身也算通过一关,所以计数器照常推进(样例 3 就是在考这个: k=1 时第一关打完之后,后面全能跳,答案只有 a_1 = 2);
- 容量是 floor((i-1)/k) 而不是 floor(i/k):进第 i 关时第 i 关还没通过, 用 floor(i/k) 会多算一个道具,样例 1 就会错成 1 而不是 4;
- 道具可以攒着不用,也可以在任意后续关卡使用,所以只有前缀数量约束, 没有「必须立刻用掉」的限制;
- n = k 时 c_n = floor((n-1)/k) = 0,一个都跳不了;
- heapq 只有小根堆,这里正好要淘汰最小值,无需取负号; heappushpop 是「先压再弹」的合并操作,比 heappush + heappop 少一次调整。
堆的用法见 35-优先队列与堆, 反悔贪心的一般套路见 47-贪心。
参考实现¶
[:octicons-arrow-left-16: BISHI48](BISHI48.md) [BISHI50 :octicons-arrow-right-16:](BISHI50.md)