p1090
一列数,两两合并,每次合并的代价是两堆的和。问将所有的堆合成一堆,最少的代价。
这道题,代码挺容易的。凭借直觉就能ac.(可以很容易的感性的认知到 先进行合并的数,累计的次数次数多。有以下贪心策略)
贪心的策略
将所有的堆放到 优先队列里面。
每次取出最小的两堆合并。
合并之后的新堆,也要push进堆里里面。
之后一直在里面重复取出最小的两堆,在push进去。
直到堆为空。
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <map>
using namespace std;
int main()
{
std::cin.tie(nullptr)->sync_with_stdio(false);
int n;cin>>n;
priority_queue<int,vector<int>,greater<int>>q;int t;
while(n--)
{
cin>>t;
q.push(t);
}
int ans=0;int tt=0;
while(!q.empty())
{
tt=q.top();
q.pop();tt+=q.top();
q.pop();
ans+=tt;
if(q.size()==0)
{
}
else
q.push(tt);
}
cout<<ans<<"\n";
return 0;
}
但是还是有必要认真严谨的去证明贪心的策略正确性。(在b站找到的一个视频)
将问题抽象出来。因为是两两合并,可以抽象成一个二叉树。
每个 节点 代表一个数字堆,当然叶子节点,代表的就是最开始的那一列数(从题目中 读入的数)。
我们可以发现,每一个数字 对结果的贡献就是:这个数*(深度-1).
我们按照 从左往右马,从下往上 的方式去 写出每个数的深度。
可以发现,后面的深度不大于前者(也就是说 深度的排列 是不降的)
所以我们要将小的数安排在前面。
至此贪心的策略证明完成。