Y
K
N
U
F

题解:P13882 [蓝桥杯 2023 省 Java A] 小蓝的旅行计划

挺可爱的反悔贪心,乍一看没看出和旅行家的预算的区别,甚至做完才发现不一样的说。

正文

首先我们可以将操作分为两个部分。分别是用油操作和加油操作。

用油

有一个简单的贪心策略,用油的时候首先使用最便宜的油,这点显然。

此外,如果当前油箱里所有油都不能到达下一站,自然就是无解,输出 \(-1\)

加油

这一部分稍微复杂点,我们显然是不太知道要加多少油的,那不妨直接全加,加到油箱满了为止,然后再把油箱里贵的油退出去,加入更便宜的油。

那可能就会导致浪费,钱算多了怎么办?

我们可以将油想象成一种只有用了才会计算价钱的东西,将花钱挪到用油的部分里面,用一点油,花一点钱就可以了。

实现细节

  1. 计算花费要开 long long。
  2. 每个站点只加一次油,而不是将油分开一点一点加,否则无法保证复杂度。

还有什么不明白的可以看代码,我这里给出一种 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_;
}   
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~
posted @ 2025-09-18 18:51  樓影沫瞬_17Hz  阅读(20)  评论(0)    收藏  举报