今天做了一道贪心算法的题,个人认为比较典型,且我在解题过程中逻辑也比较清晰,所以想写篇博客记录下自己的想法。
这是PAT甲级的一道题(PAT 甲级1033)。
以下是题目描述:
1033 To Fill or Not to Fill (25)(25 分)
With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.
Input Specification:
Each input file contains one test case. For each case, the first line contains 4 positive numbers: C~max~ (<= 100), the maximum capacity of the tank; D (<=30000), the distance between Hangzhou and the destination city; D~avg~ (<=20), the average distance per unit gas that the car can run; and N (<= 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: P~i~, the unit gas price, and D~i~ (<=D), the distance between this station and Hangzhou, for i=1,...N. All the numbers in a line are separated by a space.
Output Specification:
For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print "The maximum travel distance = X" where X is the maximum possible distance the car can run, accurate up to 2 decimal places.
Sample Input 1:
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
Sample Output 1:
749.17
Sample Input 2:
50 1300 12 2
7.10 0
7.00 600
Sample Output 2:
The maximum travel distance = 1200.00
题目大意
就是开车经过一段路程,已知起点与终点并且知道其距离D,还知道油箱的最大油量Cmax,单位汽油可以行驶的距离Davg,路途中会有N个加油站,每个加油站的油价都不同,如果到达该加油站则可以买油,最多加满油箱。需要你规划出汽车到达终点的花费最小的方法,如果不能到达终点,则输出汽车所能到达的最短距离。汽车开始在起点处,油箱油量为0.
个人解法:
其实这题一看就知道是个贪心算法的题。
我们需要做的其实就是在能力范围内同样的距离花最少的钱。
分析:
一、假如你此处在加油站k,那么在油箱加满的情况下所能到达的所有加油站中,如果有油的单价比当前油站价格低的话,令该油站编号为dest,那么我们肯定得选择用当前油站最少的油来达到dest,也就是到达dest后油箱剩余油量为0,并且dest是第一个油价比当前油站k小的油站。为什么呢?其实可以用反证法,若是到达dest后有剩余油量,那么这些油箱我们肯定在后面的路程中是要花掉的,但是我们已经到达了一个更便宜的加油站dest,dest的油价更低,所以我们为什么不用dest的油来代替上一个油站假的油呢。
二、假如你此处在加油站k,但是在你能到达的范围内没有更便宜加油站,那该怎么办呢?那么我们就要选择所能到达的加油站里油价最便宜的那个,设编号为mink,然后在k加满油再到达mink,为什么这样选择呢?其实我们可以把mink看成一个过渡的加油站,因为我们希望到达mink后,这时我们所能到达的范围内的加油站可能会更多,希望这些加油站里有比当前加油站油价比较低的油站dest,这样才能在同样的钱发挥最大的价值。
注:有个特殊情况需要注意,就是可到达的范围内没有更便宜的加油站,但是可以直接到达终点,这时我们要对终点进行判别。
其实说的这么多就是三条准则:
1、选择所能到达的范围内第一个合适的油站(即该油站价格比当前油站价格低),并且在当前油站加油到刚好能达到该合适的油站。
2、若可到达的范围内无适合油站,则将油加满,然后到达可选择的油站中油价最低的那个,但须注意一种特殊情况,也就是对能否到达终点进行判断(下面有具体代码)。
3、若可到达的范围无油站,则此时判别能否到达终点。
下面是代码
#include
#include
#define MAXN 505
#define INF 100000000
using namespace std;
double cmax, davg, dis;
int N;
struct station {
double price;
double distance;
}A[MAXN];
bool cmp(station a,station b){
return a.distance < b.distance;
}
int main() {
double range, pos = 0; //pos为当前位置的距离
double tprice; //目标油站油价
double tgas, pgas; //tgas为需要加多少油,pgas为油箱原有多少油
double res = 0; //最终结果
int k; //当前油站
bool pflag = false; //pflag表示可到达的范围内有价格较低的油站
bool dflag = false; //dflag表示可到达的范围内有油站
scanf("%lf %lf %lf %d", &cmax, &dis, &davg, &N);
for (int i = 0; i < N; i++) {
scanf("%lf %lf", &A[i].price, &A[i].distance);
}
sort(A, A + N, cmp);
if (A[0].distance != 0) { //若最近的加油站不在零点,注意该特殊情况
printf("The maximum travel distance = 0.00");
return 0;
}
range = cmax * davg;
pgas = 0;
tprice = A[0].price;
k = 0; //当前油站编号
double min = INF; //最低的油价
int mink; //最小的油站的编号
while (pos < dis) {
if (k + 1 < N) {
if (A[k + 1].distance <= (pos + range)) { //如果可到达的范围内有油站,则更新dflag
dflag = true;
}
}
if (dflag) { //第一大类情况,可到达的最大范围内有油站
for (int i = k + 1; i < N; i++) {
if (A[i].distance <= (pos + range)) {
dflag = true;
if (A[i].price < tprice) { //第一种情况:可到达的范围内有油站比当前油站油价价格低的油站
tgas = (A[i].distance - pos) / davg - pgas; //将油加到刚好可以到达该油站
res += tgas * A[k].price;
k = i;
pos = A[k].distance;
tprice = A[k].price;
pgas = 0;
pflag = true;
break;
}
}
}
if (!pflag) { //第二种情况:可到达的范围内没有价格较低的油站,但有油站可以到达
if ((pos + range) >= dis) { //如果有油站但可直接到达终点的话,对特殊情况进行判别,如果存在则可以直接返回
tgas = (dis - pos) / davg - pgas;
res += tgas * A[k].price;
printf("%.2lf", res);
return 0;
}
//需要选择可到达的范围内油价最低的油站
for (int i = k + 1; i < N; i++) {
if (A[i].distance <= (pos + range)) {
if (A[i].price <= min) { //注意是小于等于
min = A[i].price;
mink = i;
}
}
}
tgas = cmax - pgas; //将油加满
res += tgas * A[k].price;
//更新变量的值
pgas = cmax - (A[mink].distance - pos) / davg; //更新油量
k = mink;
pos = A[k].distance;
tprice = A[k].price;
}
}
if (!dflag) { //第三种情况,没有范围内可到的油站
if ((pos + range) >= dis) {
tgas = (dis - pos) / davg - pgas;
res += tgas * A[k].price;
printf("%.2lf", res);
return 0;
}
else { //不能到达终点
pos += range;
printf("The maximum travel distance = %.2lf", pos);
return 0;
}
}
pflag = false;
dflag = false;
min = INF;
}
return 0;
}
以上就是我的解法,并且AC了,其实代码冗余的地方还是有的,并且这题还有个更简单的方法,比如将终点当成一个油价为0,距离为全部距离的加油站,这样解法可以更简单一点,大家可以思考一下。
这是我第一次写技术博客,实属心血来潮,文中难免有表达不清晰、不到位的地方,还请大家见谅。