题目描述
一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离D1、汽车油箱的容量C(以升为单位)、每升汽油能行驶的距离D2、出发点每升汽油价格P和沿途油站数N(0<=N<=100),油站i离出发点的距离Di、每升汽油价格Pi(i=1,2,……N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出“No solution”。
- 输入格式 第1行:5个空格分开的数据,分别表示D1 C D2 P N 第2…N+1行:每行3个空格分开的数据,分别表示油站号i, 该油站距出发点的距离Di,该油站每升汽油的价格Pi
- 输出格式 第1行:一个数据,表示最少费用。
样例输入
275.6 11.9 27.4 2.8 2
1 102.0 2.9
2 220.0 2.2
样例输出
26.95
分析
这道题还是有难度的,在与一位大巨佬两个小时的语音通话后,总结出了AC这道题的方法,如下:主要是没看懂GM的题解
首先,这是一道贪心题:花最少的钱走完全程。现在,我们先来点简单的
改变一下题目,把油箱的容量改为无限,则贪心策略显而易见:
step1.
起点时一定要加油的,每升要用P元。假设中途有三个加油站,且你不想在中途加油,那么你的其实就是每一段都用的P价格的油
start--P--a[1]--P--a[2]--P--a[3]--P--end
如果我告诉你第二个加油站每升只要 P-1 元,你还会傻傻的用上面这个方案吗?我猜不会,你可以一开始就加刚好能到第二个加油站的油,后半程就可以换成 P-1 元的油了,如下图
start--P--a[1]--P--a[2]--(P - 1)--a[3]--(P - 1)--end
是不是赚了?我们把它一推广,就可以得到
step2.
在每个加油站加油只需加到能到达后面油价最低(且比它的油价还低)的加油站即可,若找不到这种加油站,恭喜你,可以准备直达了,中途不用换油,记:在第 i 个加油站加油用了 cost[i] 元,则有
int mi = INT_MAX, v = i;
for(int j = i + 1; j <= n; i++) {
if(a[j].p < mi && a[j].p < a[i].p) {
mi = a[j].p;
v = j
}
}
if(v == i) cost[i] = (d1 - a[i].d) / d2 * a[i].p;
else cost[i] = (a[v].d - a[i].d) / d2 * a[i].p;
不一定对哈,毕竟没认真实现过
前面的那么多都是引入,开始正题:
引入已经说的很清楚了,在这道题的正解中,我们只是多加入了一个判断无解:当在一个加油站加满油还不能到达下一个加油站时,旅行失败。
还有就是因为油箱容量有上限,所有我们还需要看一下当前油箱省的油最远能到哪一个加油站,确定选最小值的距离范围
我们还考虑了一下,距离远近顺序的问题,就是一号加油站可能在二号加油站后面,所以先有一次排序。但最后发现,数据中没有这一情况。
根据思路不难得出
AC代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 105;
struct data { // 结构体
int i;
double di, pi;
// 油站 i 离出发点的距离 Di, 每升汽油价格 Pi
} a[MAXN];
bool cmp(data x, data y) { // sort cmp 函数
return x.di < y.di;
}
int main() {
double d1, c, d2, p;
int n;
scanf ("%lf %lf %lf %lf %d", &d1, &c, &d2, &p, &n);
// 给定两个城市之间的距离 D1, 汽车油箱的容量 C, 每升汽油能行驶的距离 D2, 出发点每升汽油价格 P和沿途油站数 N
a[0].di = 0, a[0].pi = p;
for (int i = 1; i <= n; i++) {
scanf ("%d %lf %lf", &a[i].i, &a[i].di, &a[i].pi);
a[i].di /= d2;
}
a[n + 1].di = d1 / d2;
n += 2;
sort(a, a + n, cmp);
for (int i = 1; i < n; i++)
if (a[i].di - a[i - 1].di > c) { // 判断无解
printf("No solution");
return 0;
}
double rest = 0, cost;
for (int i = 0; i < n; i++) {
int min_pos = i; // 价格最低的加油站的位置
double min_val = a[i].pi; // 价格最低的加油站的价格
int j, k;
// j 剩余油量能到达的最远的加油站
// k j + 1 到 n - 1 中第一个比 minpos 便宜的点
for (j = i + 1; j < n && a[j].di - a[i].di <= rest; j++)
if (min_val < a[j].pi)
min_val = a[j].pi, min_pos = j;
// 求出价格最低的加油站的相关信息
for (k = j; k < n; k++)
if (a[k].pi < min_val)
break;
if (k == n) // 判断能否到终点
k = n - 1;
rest -= (a[min_pos].di - a[i].di);
double end = (a[k].di - a[min_pos].di);
// rest 第一部分的路程
// end 第二部分的路程
if (end <= c) { // 能直接到达 K
cost += (end - rest) * a[min_pos].pi; // 加油
if (k == n - 1)
break;
else
i = k - 1, rest = 0; // 更新
}
else { // 否则加满油
cost += (c - rest) * a[min_pos].pi;
i = min_pos;
rest = c;
rest -= a[i + 1].di - a[i].di;
}
}
printf("%.2lf\n", cost);
return 0;
}