题意: 给出n组重量为,个数的果子,每一个果子为一堆,每次可以挑两个堆合在一起,合在一起的代价是这两个堆的重量之和,求全部果子合在一个堆里的最小代价。

知识点: STL,哈弗曼编码

思路: 这题我们只需要贪心思路,想要最小代价那就每次都挑最小的两个堆合并之后把新的堆放进容器里,再找两个最小的堆合并,而这题给的个数很大,所以我们需要用二元组来存储重量和个数来减小内存负担。

所以我们的处理就变成了找到最小重量的元素,使用优先队列做容器,出队查询栈顶元素他的个数是否大于1,如果大于1,自己直接合并,两两合并完之后如果还有剩的就插入到队列里,没有就不插入,如果个数等于1,那就接着在出队查询一个栈顶元素,并把这两个合成一堆插入到容器里,接着更新第二次查询的栈顶元素是否还有剩余并做更新。

其中因为我的码力有限,所以仅提供些优化的方向,比如在输入时用map提前聚合,避免他输入

3 1
2 1
1 1

然后就直接创建了3个堆的情况,用map合并变成6 1可以防止这种输入下坑的情况。

接着就是,优先队列运行的过程中合并会直接创建一个新堆,所以如果他给出这样的样例

3 1
5 2

那么首先合并2个1,插入2 1,而不是和2 5做合并,有可能被卡内存,但是这题没卡,想要写这个合并的话map会比优先队列更好用一点,毕竟map可以直接对value操作,而不像其他容器那样需要查询。但是我拼劲全力未能写出,对容器的掌握还是太薄弱了,所以还是用优先队列写了个弱鸡版过了这题。

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define PLL pair<ll,ll>
#define PII pair<int,int>
#define endl '\n'
using namespace std;
const int m=1000000007;
int main()
{
	cin.tie(0),cout.tie(0),ios::sync_with_stdio(0);
	int n;
	cin >> n;
	priority_queue<PLL,vector<PLL>,greater<PLL>> q;
	for(int i=0;i<n;i++){
		int x,y;
		cin >> x >> y;
		q.push({y,x});
	}
 	ll ans=0;
	while(!(q.size()==1&&q.top().second==1)){
		ll w=q.top().first,v=q.top().second;
		q.pop();
		if(v>1){
			q.push({w*2,v/2});
			ans=(ans+(((w*2)%m)*((v/2)%m))%m)%m;
            if(v&1)
				q.push({w,1});
		}
		else{
			ll w1=q.top().first,v1=q.top().second;
			q.pop();
			q.push({w1+w,1});
			ans=(ans+(w1+w)%m)%m;
			if(v1-1)
				q.push({w1,v1-1});
		}
	}
	cout << ans;
	return 0;
}