https://www.acwing.com/problem/content/description/5293/
解析:
// 证明 haffman 为什么是正确的,每一次为什么可以得到最优解?
// 首先,证明为什么每次合并叶子节点的一对是最优解
// 1.如果两个节点是在同一层 && 两个叶子节点不在同一个根节点下,那么由于是同一层,所以交换两个节点的位置,结果一定不会变差
// 2.如果两个节点不在同一层 ,假设 y > z ,dy > dz
// 合并后 res = y * dy + z * dz;
// 那么如果交换 y 和 z 的位置
// res1 = z * dy + y * dz
// res - res1 = dy * (y - z) - (y - z) * dz
// ans = res - res1 = (y - z) * (dy - dz)
// 由于 y > z && dy > dz ,所以 ans > 0 , 所以说res > res1 ,也就是说交换后的结果会变得更优
// 所以将权重比较大的节点放到距离根节点比较近的距离更好
// 处理合并两堆还是三堆的问题?
// 如果合并两队,如果1 , 2 两对叶子节点在同一层,那么将2中的一个与 1 的两个合并为 3个
// 一定不会使结果变差 , 从而将 2 中的另一个合并到其父节点的父结点上,从而使层数减一,值变小
// 所以综合来讲 ,一定会比当前的解更优
// 如果 1 , 2 两对叶子节点不在同一层 d2 > d1 , 那么同上面一样的方法 ,一定会使 2 中的一个节点合并到 1 中
// 并将当前 2 中剩余的节点合并到其父节点的父节点上去 ,一定不会使结果变得更差
// 所以综上所述 ,合并 3 堆一定合并两堆更好
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
int main()
{
int n;
cin >> n;
priority_queue<LL , vector<LL> , greater<LL>> heap;
for(int i = 0 ; i < n ; i ++)
{
int x;
cin >> x;
heap.push(x);
}
if(n % 2 == 0) heap.push(0);
LL cost = 0;
while(heap.size() > 1)
{
LL sum = 0;
for(int i = 0 ; i < 3 ; i ++)
{
LL x = heap.top();
heap.pop();
sum += x;
}
heap.push(sum);
cost += sum;
}
cout << cost << endl;
return 0;
}