poj 2431 Expedition 优先队列

缘起

日常水题 poj 2431 Expedition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
需要驾驶一辆卡车行驶L距离. 伊始,卡车上有P单位汽油. 卡车
每开1单位距离就消耗1单位汽油. 如果卡车在路途中耗尽汽油
将无法继续前行. 因而无法抵达终点. 途中N个加油站,第i个加油站在距离终点Ai单位距离的地方,最多可以供给卡车Bi单位汽油.
假设油箱无限大. 请问卡车能否抵达终点? 如果可以,最少需要
加多少次油? 输出最少加油次数, 如果不能抵达终点,输出-1

【输入】
第一行是N(1 <= N <= 10000),后面N行是Ai(1<=Ai<L, 1<=Bi<=100)和Bi, 最后一行是L(1<=L<=1000000)和P(1 <= P <= 1000000)

【输出】
最少加油次数或者-1

【限制】
Time limit 1000 ms
Memory limit 65536 kB

【来源】
USACO 2005 U S Open Gold

本题真可谓是沿用了UVA一贯的风格~ 脑经急转弯~ 想到了就简单无比,没想到,就极度复杂~

一开始我的思路是错误的,使用了dp.

1
2
3
dp[i][j]=到达i号加油站剩下至少j油量最少需要的加油次数. 那么根据上一站是0(起点),1,...,i-1得到如下
dp方程
dp[i][j] = 1+min{dp[0][max(0,A0-Ai+j-B0)],...,dp[i-1][max(0,A_{i-1}-Ai+j-B_{i-1})]}

但是这个dp方程无论如何也很难化简. 所以要变换思路——其实如果汽车路过加油站i 油量尚够的话, 不需要急着加油, 可以将此次加油的量保存起来,后面不够到达下一站的时候再拿出来用. 所以每次拿出来的时候要拿出迄今为止最大的(这样才能保证加油次数最少)——所以堆是维护这种数据结构的最好方式. 不过注意,题目给的加油站的数据未必是有序的. 所以要先排一下序.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//#include "stdafx.h"

#include <stdio.h>
#include <algorithm>
#include <queue>
using namespace std;
//#define LOCAL

const int maxn = 10005;
int n,L,P;

priority_queue<int, vector<int>, less<int> > pq; // 大根堆

struct Gass
{
int a,b; // a是此加油站到达终点站的距离, b是此加油站可以提供的油量
bool operator <(Gass x)
{
return a>x.a;
}
}g[maxn];

int sz() // 做的事情都是来到了一个加油站,然后看看现在手上有多少油, 如果不够到下一个加油站(这里将终点看作是一个加油站),就从pq中获取最大值进行加油(加油次数+1), 如果依旧不够,则打出-1(无法抵达终点)
{
int i = 0,ans=0; // i是当前到达的加油站,ans是最少加油次数
while(i<n+1) // 如果还没有到达终点站
{
pq.push(g[i].b); // 本次加油的量也要进堆
if (P>=(g[i].a-g[i+1].a)) // 如果当前剩油P可以支撑走到下一站
{
P-=(g[i].a-g[i+1].a); // 剩油量
}
else // 如果不够支撑到下一站, 就要从迄今为止(包括当前加油站)保存的加油机会中选出最大的
{
while(!pq.empty() && P<(g[i].a-g[i+1].a)) // 只要还有存货,而且还不够,注意, 不能只拿一次, 例如初始油量是2, 然后第一个加油站距离起点是1, 第二个加油站距离起点是2,第三个加油站距离起点是102, 第一个加油站供油量是50, 第二个加油站供油量也是50. 那么如果只取一次的话, 就会判断无法到达终点,这显然是错误的.
{
int top = pq.top();
pq.pop();
P+=top;
ans++; // 加了一次油
}
if (P<g[i].a-g[i+1].a) // 如果还不行
{
return -1;
}
P-=g[i].a-g[i+1].a;
}
i++; // 跑到下一站去了
}
return ans;
}

int main()
{
#ifdef LOCAL
freopen("d:\\data.in", "r", stdin);
//freopen("d:\\my.out", "w", stdout);
#endif
scanf("%d", &n);
for (int i = 1; i<=n; i++)
{
scanf("%d%d", &g[i].a, &g[i].b);
}
scanf("%d%d", &L, &P);
g[0].a = L,g[0].b = 0, g[n+1].a = 0,g[n+1].b = 0;
sort(g, g+n+2); // 排序
int ans = sz();
if (~ans)
{
printf("%d\n", ans);
}
else
{
puts("-1");
}
return 0;
}

ac情况

Status Accepted
Time 47ms
Memory 224kB
Length 1347
Lang C++
Submitted 2019-08-21 09:17:29
Shared
RemoteRunId 20778387