重新分装
重新分装
有 种零件,编号 。
第 种零件的数量为 。
有 个箱子,编号 。
初始时,所有零件都装在第 个箱子里。
我们希望将所有零件重新分装,使得最终对于每个 ()都满足,所有的第 种零件都装在第 个箱子里。
为此,你可以进行任意次(也可以不进行)分装操作,每次操作分为以下步骤:
- 第一步,任选一个非空箱子,并拿出该箱子中的所有零件。
- 第二步,任选 个空箱子(在上一步中被清空的箱子也可以选),将在上一步中拿出的所有零件重新分装到这些被选中的空箱子中,你可以随意分配这些零件的归属,但是要求每个被选中的空箱子都至少需要被放入一个零件。
每次操作所需付出的代价等于该次操作第一步中拿出的零件的数量。
请你计算,为了完成目标所需付出的总代价的最小可能值。
输入格式
第一行包含整数 。
第二行包含 个整数 。
输出格式
一个整数,表示总代价的最小可能值。
数据范围
前 个测试点满足 。
所有测试点满足 ,。
输入样例1:
3
1 2 3
输出样例1:
6
输入样例2:
4
2 3 4 5
输出样例2:
19
解题思路
实际上最后并不需要保证第 种零件恰好在第 个箱子,只需保证每个箱子都有 种零件即可。因为最后的代价只取决于每次取出的零件数量,与放入哪个箱子无关。当然在这个过程中一定可以通过调整的方法将第 种零件恰好放在第 个箱子。
因此我们只用关心如何将一堆零件拆分成 堆使得代价最小。反过来看,这个过程等价于如何将 堆零件合并成 堆使得代价最小。如果每次只能选两堆进行合并,那么这就是一个很经典的贪心问题,即每次选择权值最小的两堆进行合并,经过 次合并后就会得到 堆。整个合并过程可以构成出一棵 Huffman 树。
但现在每次可以选择 堆进行合并,直觉上想应该是能选择 堆进行合并就尽可能选择。这是因为在每次选择两堆进行合并所得到的 Huffman 树中,总代价就是每个叶子节点的权值乘上该叶子节点到根节点的距离的总和。因此如果每次选择 堆合并,那么 Huffman 树的分枝就会变多,意味着整棵树的高度就会变低,总代价就会变小。
考虑是否每次都能选择 堆进行合并呢?如果每次都选择 堆合并,那么每次合并后堆的数量就会减 ,由于最后只剩下 堆,因此如果 是偶数(即 是奇数)则可以这么做。否则到最后一定会恰好得到两堆,这时只能选择两堆进行合并。
实现的话只需用小根堆去模拟这个过程即可,每次取出堆顶的 个元素进行合并。另外为了方便,如果 是偶数则往堆中压入一个 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int n;
scanf("%d", &n);
priority_queue<LL, vector<LL>, greater<LL>> pq;
for (int i = 0; i < n; i++) {
int x;
scanf("%d", &x);
pq.push(x);
}
LL ret = 0;
if (~n & 1) pq.push(0);
while (pq.size() > 1) {
LL t = 0;
for (int i = 0; i < 3; i++) {
t += pq.top();
pq.pop();
}
ret += t;
pq.push(t);
}
printf("%lld", ret);
return 0;
}
参考资料
AcWing 5290. 重新分装(AcWing杯 - 周赛):https://www.acwing.com/video/5253/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17959663
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-01-11 Maximum Number of Points From Grid Queries
2023-01-11 Frog Jump II