原题地址
http://poj.org/problem?id=2431
题意:一辆有P单位汽油的卡车,刚开始停在距离起点处,距离终点L单位距离,卡车每开一单位距离就要消耗一单位汽油。在途中一共有N个加油站,每个加油站有各自的位置和油量。假设卡车的燃料箱容量无限,请问最少需要加几次油到达终点。如果不能到达则输出-1。
解题思路
这道题需要一个切入点,有了这个思想,贪心的做法写起来就很简单了。
在卡车开往终点的途中,只有在加油站可以加油,转换一下思路,每经过一个加油站 i,就获得了一次在之后的任何时候都可以加 i 的油的权利,也就是把 i 作为了储备的油,之后需要加油的时候,就认为是在之前经过的加油站里加油就可以了(无疑是加满)。
因为希望停的次数尽可能少,所以当燃料为0了之后再进行加油、且选经过的油站里油量最大的加,是很好的办法。每次贪心取最大,数据结构上最合适的就是最大堆形式的优先队列啦。
实现时,先对所有油站的距离排序(给出的是距离终点的距离,先转换为距离起点的距离),每经过一个油站就把该油站的油量加入优先队列,当没油时,如果优先队列已经空了,说明之前的油都加完了,否则取出最大数值加油。
编码了两种实现,都可以AC:
- 《挑战程序设计竞赛》书上给出的代码solve_book(),它以每次能否到达下一个加油站为判断标准(其实不是很懂?和它描述的思路不那么对应)。有个处理技巧,将终点也作为一个空的加油站,做循环的时候就不用再处理边界条件。
- 自己按照思路实现的代码solve_mine()。每次把油耗尽之后再回头看是否还有油可以加,而不是判断能否到达下一个加油站。
AC代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 10005;
typedef struct node
{
int dist, fuel;
bool operator < (const node A) const //距离升序
{
return this->dist < A.dist;
}
}NODE;
int n, length, left_fuel;
NODE stop[maxn];
void solve_book() //书上代码:以能否到达下一个加油站为判断条件
{
int pos = 0, pass = 0;
//把终点也当作一个空油站
stop[n].dist = length; stop[n].fuel = 0;
n++;
sort(stop, stop+n); //按距离排序
priority_queue<int> pq;
for(int i = 0; i<n; ++i)
{
int between = stop[i].dist - pos; //距离下一个加油站
while (left_fuel < between) //到不了新的加油站
{
if (pq.empty()) //队列已空,没储备油可加
{
cout << -1 << endl;
return;
}
left_fuel += pq.top();
pq.pop();
++pass; //加油次数+1
}
//到达新的加油站
pos = stop[i].dist;
left_fuel -= between;
pq.push(stop[i].fuel); //加入储备油站
}
cout << pass << endl;
}
void solve_mine() //自己实现:每次开到最远,再回头看油
{
sort(stop, stop+n); //按距离排序
priority_queue<int> pq;
int pos = 0, index = 0, ans = 0; //index-1指向开过的最右边站点
while(1)
{
pos += left_fuel; //当前油量开到的最远点
if (pos >= length) //能开过终点
{
cout << ans << endl;
return;
}
while (stop[index].dist <= pos) //统计此次开过的站
{
pq.push(stop[index].fuel);
index++;
}
if (pq.empty()) //无油可加
{
cout << -1 << endl;
return;
}
//加队列里的最大油量
left_fuel = pq.top(); pq.pop();
ans++;
}
}
int main()
{
//freopen("C:/Users/DELL/Desktop/a.txt", "r", stdin);
ios::sync_with_stdio(false);
cin >> n;
for (int i = 0; i<n; ++i)
cin >> stop[i].dist >> stop[i].fuel;
cin >> length >> left_fuel;
for (int i = 0; i<n; ++i) //转换为到起点的距离
stop[i].dist = length - stop[i].dist;
//solve();
solve_mine();
return 0;
}
算法复杂度:O(n)
耗时:141ms