本文专注于记录我如何使用AI完成算法刷题练习,以此来分享如何高效利用AI刷题。
#38补给站最优花费问题
问题描述
小明想从A徒步到B,总路程需要M天,路程中为了确保安全,小明每天需要消耗1份食物。
在起点及路程当中,零星分布着N个补给站,可以补充食物,不同补给站的食物价格可能不同。
请问小明若要安全完成徒步,最少需要花费多少钱呢?
输入格式
第一行为两个正整数M、N,代表总路程M天,补给站个数N
接下来N行,每行有两个非负整数A、B代表一个补给站,表示第A天经过该补给站,每份食物的价格为B元。
A是从0开始严格递增的,即起点一定有补给站,补给站是按位置顺序给出的,且同一个位置最多有一个补给站。
输出格式
输出一个整数,表示最少花费的金额
输入样例
5 4
0 2
1 3
2 1
3 2
输出样例
7
说明:在第0天买2份食物,在第2天买3份食物,共花费7元
数据范围
- 30%的数据,
N <= M <= 100,0 <= A < M,0 <= B <= 1000 - 80%的数据,
N <= M <= 10000,0 <= A < M,0 <= B <= 1000 - 100%的数据,
N <= M <= 1000000,0 <= A < M,0 <= B <= 1000
思路
明确输入,输出和处理。
输入
- M:总路程的天数,表示小明需要徒步的天数。
- N:补给站的个数。
- 补给站数组:一个二维数组
p,其中每个元素是一个长度为2的数组[A, B],表示在第A天有一个补给站,每份食物的价格为B元。
输出
- 最少花费的金额:一个整数,表示小明完成徒步所需的最少花费。
处理
- 设计一个数据结构存储进行存储。
- 设计一个算法遍历补给站,该算法时间复杂度要尽可能低,满足数据量在百万内的需求。
算法设计
读完题目,可以发现用贪心算法来解决,目标是通过最少花费确保每天有足够的食物完成旅程。
-
关键点:每天必须有食物才能继续前进,因此需要在路途中每经过一个补给站时,决定购买多少食物。
-
贪心策略:
- 尽可能选择食物价格最低的补给站购买。
- 考虑当前库存是否足够支持到下一个补给站,若不足,则在当前补给站补充。
- 避免携带过多食物以减少花费。
-
算法步骤:
- 初始化变量:当前库存
current_food为0,总花费cost为0。 - 遍历补给站数据,计算到下一个补给站所需的食物量。
- 如果库存不足,补充所需食物并更新花费。
- 遍历完成后确保到达终点,若终点还有天数未覆盖,则补充足够的食物。
- 初始化变量:当前库存
代码实现
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int solution(int m, int n, vector<vector<int>> p) {
int current_food = 0; // 当前库存
int cost = 0; // 总花费
int current_day = 0; // 当前天数
// 在补给站后增加一个虚拟终点站
p.push_back({m, 0});
// 优先队列用于存储 {价格, 可购买最大数量}
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
for (const auto &station : p) {
int day = station[0];
int price = station[1];
// 需要到达该补给站需要的食物量
int days_to_next = day - current_day;
// 消耗当前库存
while (current_food < days_to_next) {
if (pq.empty()) return -1; // 无法补充足够食物,无法完成
// 贪心选择当前最低价格
auto [min_price, available_food] = pq.top();
pq.pop();
int needed = days_to_next - current_food;
int buy = min(needed, available_food);
cost += buy * min_price;
current_food += buy;
// 如果还有剩余的食物,把剩余数量放回队列
if (available_food > buy) {
pq.push({min_price, available_food - buy});
}
}
// 更新库存和天数
current_food -= days_to_next;
current_day = day;
// 将当前补给站食物加入优先队列
if (price > 0) {
pq.push({price, m - day}); // 假设可以购买足够到终点的数量
}
}
return cost;
}
int main() {
// 测试样例
cout << solution(5, 4, {{0, 2}, {1, 3}, {2, 1}, {3, 2}}) << endl; // 输出 7
return 0;
}
代码说明
-
优先队列:使用
priority_queue存储当前可选的食物价格及库存,确保每次选择最低价格的食物。 -
库存更新:在每次到达补给站前计算需要补充的食物量,利用贪心策略从最低价优先购买。
-
特殊情况:如果无法满足所需食物量,直接返回
-1。
时间复杂度
- 遍历补给站:O(N)
- 优先队列操作:每次插入或删除操作为O(logN)
- 总复杂度约为:O(NlogN)
空间复杂度
- 主要为优先队列的存储:O(N)