这篇文章上次修改于 558 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

题目传送门

0x01 简化题意

路上有 $n$ 个加油站,每个加油站有一定量的油,卡车邮箱无限大,每 $1$ km需要 $1$ 箱汽油,问最少需要加油几次到达终点。

0x02 思路分析

简单看一下数据范围,不是很小,题目求最少加油几次才能到达终点,只要稍加分析就能发现是贪心,既然我在这里停了,那一定就是要把所有油加满跑到下一个加油站,但是如果直接选择能走到的加油站就可能出现走不到下一个加油站的情况。这种情况呢就要从前面经过但没有停的加油站中选一个来加油了。

0x03 别慌着打代码啊

好复杂,之前那个思路一会儿又要向前走,一会儿又要向后走,如何统一一下呢,很简单,只用维护当前已走过的加油站还没加油的最小值,优先队列(堆)!,只用将队头取出,再累计进可以走到的距离,然后再向优先队列中推入又能够走到的加油站就好了,具体地看看代码吧。

0x04 Code

#include <queue>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){
		c=='-'?f=-1:1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<3)+(x<<1)+(c^48);
		c=getchar();
	}
	return f*x;
}
class node{
	public:
		int length,how;
	node(){
		length=how=0;
	}
};
bool operator<(node a,node b){
	return a.how<b.how;
}
bool cmp(node a,node b){
	return a.length<b.length;
}
int main(){
	//	freopen("expedition.in","r",stdin);
	//	freopen("expedition.out","w",stdout);
	int t=read();
	for(int abc=1;abc<=t;abc++){
		priority_queue<node>a;
		node b[10005];
		memset(b,0,sizeof(b));
		int n;
		n=read();
		for(int i=1;i<=n;i++){
			b[i].length=read();
			b[i].how=read();
		}
		int l,p;
		l=read();p=read();
		for(int i=1;i<=n;i++){
			b[i].length=l-b[i].length;
		}
		int i1=1;
		int use=0;
		sort(b+1,b+n+1,cmp);
		bool have=1;
		while(p<l){
			use++;
			for(;i1<=n;i1++){
				if(b[i1].length<=p){
					a.push(b[i1]);
				}else{
					break;
				}
			}
			if(a.size()>0){
				p+=a.top().how;
				a.pop();
			}else{
				cout<<-1<<endl;
				have=0;
				break;
			}
		}
		if(have)
			cout<<use<<endl;
	}
	return 0;
}

0x05 后话

有问题欢迎在下面提出,感觉这道题的贪心性质是非常明显的,但是有一点坑。