题目链接
[蓝桥杯 2025 省 Python A/Java A/研究生组] 原料采购
题目描述
小蓝负责一家工厂的原料采购。
工厂有一辆运货卡车,其容量为 m m m。
工厂附近的采购点都在同一条路的同一方向上,一共有 n n n 个,每个采购点和工厂的距离各不相同。其中,第 i i i 个采购点的价格为 a i a_i ai, 库存为 b i b_i bi, 距离为 c i c_i ci。
卡车每行驶一单位长度的路径就需要额外花费 o o o。(返程没有花费,你也可以认为 o o o 实际是行驶两单位长度的花费)
请计算将卡车装满最少需要花费多少钱,如果没有任何方案可以装满请输出 − 1 -1 −1。
输入格式
输入的第一行包含三个正整数 n , m , o n, m, o n,m,o,相邻整数之间使用一个空格分隔。
接下来 n n n 行,每行包含三个正整数 a i , b i , c i a_i, b_i, c_i ai,bi,ci 表示一个采购点,相邻整数之间使用一个空格分隔。
输出格式
输出一行包含一个整数表示答案,即装满卡车所需的最小花费。
输入输出样例 #1
输入 #1
3 5 1
99 9 1
3 4 99
1 2 190
输出 #1
201
说明/提示
评测用例规模与约定
- 对于 40 % 40\% 40% 的评测用例, n ≤ 5000 n \leq 5000 n≤5000, m ≤ 50000 m \leq 50000 m≤50000;
- 对于 60 % 60\% 60% 的评测用例, m ≤ 1 0 5 m \leq 10^5 m≤105;
- 对于所有评测用例, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105, 1 ≤ m , o ≤ 1 0 9 1 \leq m, o \leq 10^9 1≤m,o≤109, 1 ≤ a i , b i , c i ≤ 1 0 9 1 \leq a_i, b_i, c_i \leq 10^9 1≤ai,bi,ci≤109, 保证对于 i > 1 i > 1 i>1, 一定有 c i − 1 < c i c_{i-1} < c_i ci−1<ci。
前言
在考试的时候,因为这是最后一道题目,我以为这道题会很难,而且时间不多了,所以我直接放弃了这道题,去检查了前面几道题目。考试完之后,尝试了这道题,发现就是反悔贪心的模板题。这个事情告诉我们,不要把题目想得太难,“自己吓自己”,要去勇敢地尝试。
问题分析
卡车需采购足够货物,每个采购点有价格、库存、距离。每次运输的最远距离决定运输费用(距离×单位成本)。目标是在满足库存需求下,最小化总成本(采购+运输)。
解题思路
按距离由近到远遍历采购点,确保处理到第
i
i
i个点时,运输费用就是当前采购点的距离乘以
o
o
o,这样我们就可以把运费给独立出去,在贪心时,只考虑购买原料需要的花费。
按距离由近到远采购原料,把选出来的原料放进优先队列里由高到低降序排列(为了提高效率,采用大顶堆)。当我们的原料未满的时候,用当前的原料填充,放入大顶堆,当我们的原料满了的时候,将当前原料的价格与已选原料中最高的价格进行比较,如果当前价格低于已选原料,我们就反悔一次,进行替换。这实质上是一个反悔贪心。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll INF = 2e18 + 7; // 无穷大值,表示无法达到的状态
const int N = 1e5 + 7;
struct Procurement {
ll price; // 采购价格
ll stock; // 库存量
ll distance; // 距离
// 重载运算符,优先队列按价格降序排列(最大堆)
bool operator<(const Procurement& other) const {
return price < other.price;
}
} nodes[N];
ll min_cost = INF; // 记录最小总成本
ll current_stock = 0; // 当前已采购的库存
ll total_cost = 0; // 当前总采购成本(不含运输)
ll n, m, o; // 采购点数量、需求总量、单位运输成本
priority_queue<Procurement> max_heap; // 维护已选的高价货物,便于替换
int main() {
cin >> n >> m >> o;
for (int i = 1; i <= n; ++i) {
cin >> nodes[i].price >> nodes[i].stock >> nodes[i].distance;
if (current_stock + nodes[i].stock <= m) { // 全部购入当前点
total_cost += nodes[i].stock * nodes[i].price;
current_stock += nodes[i].stock;
max_heap.push(nodes[i]);
}
else { // 部分购入或替换之前的高价货物
if (current_stock < m) { // 先补满需求
ll needed = m - current_stock;
total_cost += needed * nodes[i].price;
current_stock = m;
// 将购入的部分加入堆,剩余库存更新
max_heap.push({ nodes[i].price, needed, nodes[i].distance });
nodes[i].stock -= needed;
}
// 尝试用当前低价替换堆中的高价货物
while (!max_heap.empty() && nodes[i].stock > 0) {
Procurement top_node = max_heap.top();
if (top_node.price <= nodes[i].price) break; // 无法优化
max_heap.pop();
if (top_node.stock <= nodes[i].stock) { // 全部替换
total_cost -= (top_node.price - nodes[i].price) * top_node.stock;
nodes[i].stock -= top_node.stock;
max_heap.push({ nodes[i].price, top_node.stock, nodes[i].distance });
}
else { // 部分替换
total_cost -= (top_node.price - nodes[i].price) * nodes[i].stock;
max_heap.push({ nodes[i].price, nodes[i].stock, nodes[i].distance });
// 剩余部分重新入堆
max_heap.push({ top_node.price, top_node.stock - nodes[i].stock, top_node.distance });
nodes[i].stock = 0;
}
}
// 更新最小成本:当前总采购成本 + 当前点运输成本
/*如果没有购买i点的原料,运费就不是nodes[i].distance* o,那么这里写 nodes[i].distance* o,会对答案有影响吗?
答案肯定是没有的。因为如果没有购买i点的原料,那么肯定存在total_cost + nodes[k].distance * o(k<i);
total_cost + nodes[k].distance * o(k<i)肯定小于total_cost + nodes[i].distance * o,
ans就等于total_cost + nodes[k].distance * o(k<i),仍然正确*/
min_cost = min(min_cost, total_cost + nodes[i].distance * o);
}
}
if (current_stock < m) cout << -1 << endl; // 无法满足需求
else cout << min_cost << endl;
return 0;
}