给雷暴磕头了/kt 给雷暴磕头了/kt 给雷暴磕头了/kt
求组合前 k 大用堆贪心,需要构造转移使得状态之间成为一个外向树,并且转移不重不漏,而且一个状态的后继很少。这样用堆贪心贪出来即可。
P1
给定非负整数构成的多重集,求前 小子集和。
从小往大排序,想用“先移动最右边的到指定位置,再移动最左边的到指定位置......”这个策略去描述状态的转移。首先选把选了前 个的状态 push 到堆里面。发现描述转移的时候需要记录的信息有:前缀 还没有移动,当前正在移动的物品位于 ,上个物品停在了 .转移就是 继续往后一格但是不能越过 ,或者 彻底停住再去移动 .
P2
个正整数数组,一个方案的代价是每个数组里面恰好选一个数再求和,求前 小的方案的代价。P2541。
设计策略是挨个数组确定选第几个。也就是 表示选到了第 个数组,当前这个数组选的是第 个人.但是发现计重了, 被 和 两个都算到了。原因在于 可以为 ,但是后缀还没决策的数组选的全是 ,于是对于一个状态来说无法唯一确定它的 .
于是强制让 ,那么转移就分三种:
- 当前数组从第 个换成第 个;
- 换到下一个数组,从第 个开始选;
- 如果当前 ,说明可以撤销回 ,然后从下一个数组的第 个开始选。
为了让第三个转移非负,按照 将所有 排序即可。
除了全选第一个的最优解以外,其余的均能转移到并且仅被转移一次(按照这个策略去尝试把一个状态凑出来,只有一种方案)。
P3
P2 改成每个数组要选数量在 中的若干个数。P6646。
对于单个数组,用 P1 中的方法已经可以求出前 小的方案,看成一个黑箱,结合 P2 做法即可。
P4
给定非负整数 ,对于排列 代价为 ,求前 小的代价和。
最优就是 降序排序之后 ,然后考虑一个排列从后往前插排去得到。这个时候状态记录 为现在从后往前插排在将 往后移动移到了第 个位置。同 P2 中的问题一样,当 的时候会出现问题,但是套用 P2 中的做法,先强制 ,然后再考虑撤销往前也不行,因为这里并不像 P2 中一样可以交换数组之间顺序使得转移非负。
这里转移非负的问题就在于,为了剪枝掉后继个数,当前的 已经插排完毕,选择下一个进行插排的 的时候想直接通过对 的状态进行撤销再选择另一个 (形象一点的描述就是,运用了儿子兄弟表示法)。思路往回推,记录当前已经插排完毕的是哪个 ,选择下一个 的时候从最优开始,最优完了再找次优,次优完了再往后找...
于是记录 表示已经考虑完了 的所有位置, 还没有尝试插排。现在有 .如果继续移动 那么直接正常做;如果当前的 移动完了,去找下一个 ,对 里面每个 记录 表示下一个交换 多出来的代价是多少,那么找 最小的那个位置当作下一个 ,此时让 pre 不变。
那么当 的时候就意味着当前状态的 是尝试新开一个 得到的状态,那么它的转移就多了一个撤销当前的 ,尝试比它次优的那个 ,于是需要找到按 排序之后它的后继。只有 不是 ,其余的 均为 ,所以尝试新的 的时候单独把 作为一个转移拎出来,现在 ,所以直接在主席树上二分即可找到后继。
整理一下现在的转移:
当 时:
- 继续移动 ,此时 , 被强制更新为 ;
- 选择下一个 ,并且选择的就是 ,此时 被强制更新为 ;
- 选择下一个 ,选择的是 中的,此时 应选择 最小的, 不变。
当 时:
- 同上;
- 同上;
- 类似,选择下一个 ,选择的是 中的,此时 应选择 最小的,然后 被更新为 .
- 撤销当前选的 , 选为 里面选当前的 的后继, 不变。
还需要维护各个转移需要的代价,由于只有交换操作,所以用个可持久化线段树就行了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?