题解:P13882 [蓝桥杯 2023 省 Java A] 小蓝的旅行计划
挺可爱的反悔贪心,乍一看没看出和旅行家的预算的区别,甚至做完才发现不一样的说。
正文
首先我们可以将操作分为两个部分。分别是用油操作和加油操作。
用油
有一个简单的贪心策略,用油的时候首先使用最便宜的油,这点显然。
此外,如果当前油箱里所有油都不能到达下一站,自然就是无解,输出 \(-1\)。
加油
这一部分稍微复杂点,我们显然是不太知道要加多少油的,那不妨直接全加,加到油箱满了为止,然后再把油箱里贵的油退出去,加入更便宜的油。
那可能就会导致浪费,钱算多了怎么办?
我们可以将油想象成一种只有用了才会计算价钱的东西,将花钱挪到用油的部分里面,用一点油,花一点钱就可以了。
实现细节
- 计算花费要开 long long。
- 每个站点只加一次油,而不是将油分开一点一点加,否则无法保证复杂度。
还有什么不明白的可以看代码,我这里给出一种 multiset 的实现。
代码
// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
using namespace std;
#define getc() getchar_unlocked()
#define putc(a) putchar_unlocked(a)
#define en_ putc('\n')
#define e_ putc(' ')
using lint = long long;
using pii = pair<int, int>;
template<class T> inline T in() {
T n = 0; char p = getc();
while (p < '-') p = getc();
bool f = p == '-' ? p = getc() : 0;
do n = n * 10 + (p ^ 48), p = getc();
while (isdigit(p));
return f ? -n : n;
}
template<class T> inline T in(T &a) { return a = in<T>(); }
template<class T> inline void out(T n) {
if(n < 0) putc('-'), n = -n;
if(n > 9) out(n / 10);
putc(n % 10 + '0');
}
const int N = 2e5 + 10;
signed main() {
#ifndef ONLINE_JUDGE
freopen("i", "r", stdin);
freopen("o", "w", stdout);
#endif
int n = in<int>(), m = in<int>(), sum = m, dis, c, lim; // sum 是当前油量
lint cost = 0;
// 不用 set 因为 set 会去重(虽然这题没差)
multiset<pii> e; // 用 pair,第一维用来排序,是价格,第二维自然就是油量
e.insert({0, m}); // 给不会 set 的补充一下:insert 用来给 set 插入东西
for(int i = 1; i <= n; i ++) {
in(dis), in(c), in(lim);
while(dis != 0) {
if(e.empty()) { // 油箱空了就无解
out(-1);
exit(0);
}
int t = e.begin()->second, k = e.begin()->first, red = min(dis, t);
e.erase(e.begin()); // begin 返回 set 中最小值的指针
//erase 用来删除 set 的中某值或某指针的所在
dis -= red, t -= red, sum -= red;
cost += 1ll * k * red;
if(t) e.insert({k, t}); // 这几行是跑路的过程,注意实现细节。
}
int ine = 0; // 记录加多少
while(lim != 0) {
if(sum < m) {
int add = min(lim, m - sum); // 油箱没满就加满
lim -= add, sum += add;
ine += add;
}
else {
if(e.empty()) break; // 不然就用便宜油代替贵油
int t = e.rbegin()->second, k = e.rbegin()->first, red = min(lim, t);
if(k <= c) break; // rbegin == end - 1,即最大值
e.erase(e.find(*e.rbegin()));
// 这里先 find 再 erase 是为了只删一个,否则会删除某个值的所有所在
t -= red, lim -= red;
ine += red;
if(t) e.insert({k, t});
}
}
e.insert({c, ine}); // 必须只加一次,否则复杂度无保证
}
out(cost); en_;
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~