题目题意:
我们有n种种子,每种种子有c个且每种种子中每个都为w重,然后每个种子算作一堆,假设一共有m堆种子,我们的任意一次操作可以使任意两堆种子合并为一堆,直至仅剩下一堆,即执行m-1堆,且代价值=合并前两堆种子重量之和,即合并后的重量,要求计算出代价值之和的最小值。
题目知识点:
贪心、模运算、数据结构
题目分析:
这题让我们通过安排合并种子的顺序,从而使代价值总和最小。
那么我们可以想象一下,如果让最大值不论与最小值还是那个进行合并,得到的代价值只会比最大值还要大,所以,我们自然就能想到只有去合并不断更新后的堆里面的两个最小值,才能使代价值归为最小。
而且不同种子的重量有可能相同,我们可以用一个map来合并相同重量的种子,这样会避免超过空间限制
我们可以将相同重量的种子们一次性先进行合并,从而避免超时。
有了这样一个思路后,我们便可以写代码了:
先录入数据并合并相同重量的种子
int n;
ll sum = 0, ans = 0; //sum用来计算总共有多少堆种子,ans用来计算答案
map<ll, ll>mp; //map来合并相同重量的种子
cin >> n;
for (int i = 1; i <= n; i++) {
ll c, w;
cin >> c >> w; //录入数据
mp[w] += c;
sum += c;
}
然后到了最核心的部分:
while (sum > 1) {
auto [w1, c1] = *mp.begin();
mp.erase(mp.begin());
if (c1 >= 2) { //该if用来一次性合并相同重量的种子
mp[2 * w1] += c1 / 2;
sum -= c1 / 2;
ans = (ans + (2 * w1 % mod) * (c1 / 2 % mod)) % mod; //更新ans
if (c1 % 2==0) continue; //如果该重量的种子个数为偶数,一定会被合并完毕,那么就直接考虑下一个重量
}
if(mp.empty()) break;
auto [w2, c2] = *mp.begin();
mp[w1 + w2]++; //将合并后的重量加一
ans = (ans + w1 + w2) % mod; //更新ans
mp[w2]--;
if (!mp[w2]) mp.erase(mp.begin()); //如果w2重量的种子被合并完后,就被剔除
sum--;
}
完整代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
void solve() {
int n;
ll sum = 0, ans = 0;
map<ll, ll>mp;
cin >> n;
for (int i = 1; i <= n; i++) {
ll c, w;
cin >> c >> w;
mp[w] += c;
sum += c;
}
if (sum == 1) {
cout << "0" << endl;
return;
}
while (sum > 1) {
auto [w1, c1] = *mp.begin();
mp.erase(mp.begin());
if (c1 >= 2) {
mp[2 * w1] += c1 / 2;
sum -= c1 / 2;
ans = (ans + (2 * w1 % mod) * (c1 / 2 % mod)) % mod;
if (c1 % 2==0) continue;
}
if(mp.empty()) break;
auto [w2, c2] = *mp.begin();
mp[w1 + w2]++;
ans = (ans + w1 + w2) % mod;
mp[w2]--;
if (!mp[w2]) mp.erase(mp.begin());
sum--;
}
cout << ans << endl;
}
int main() {
int t = 1;
while (t--) {
solve();
}
}

京公网安备 11010502036488号