POJ 2431 Expedition(贪心+优先队列)

原题地址

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值