[蓝桥杯 2025 省 Python A/Java A/研究生组] 原料采购 题解

题目链接

[蓝桥杯 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 n5000, m ≤ 50000 m \leq 50000 m50000;
  • 对于 60 % 60\% 60% 的评测用例, m ≤ 1 0 5 m \leq 10^5 m105;
  • 对于所有评测用例, 1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1n105, 1 ≤ m , o ≤ 1 0 9 1 \leq m, o \leq 10^9 1m,o109, 1 ≤ a i , b i , c i ≤ 1 0 9 1 \leq a_i, b_i, c_i \leq 10^9 1ai,bi,ci109, 保证对于 i > 1 i > 1 i>1, 一定有 c i − 1 < c i c_{i-1} < c_i ci1<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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值