题目链接:
[NOIP1999 提高组] 旅行家的预算 - 洛谷https://www.luogu.com.cn/problem/P1016
思路:
贪心思路,对于每一个加油站,如果能在满油情况下到达一个更便宜的加油站,就考虑只加到刚好能到这个加油站的油(但是有一种特殊情况,如果当前油已经能到那里,那就不用加了,这个要特别注意)。而如果在满油情况也到不了更便宜的加油站,那就在当前尽量多加油,直到刚好能到达终点。
这道题思路不算很难,但是代码有点难写,我写了2个小时啊啊orz,可能是我太菜了吧555。
#include <iostream>
using namespace std;
const int maxn = 10;
struct point{
double pos = 0;
double price = 0;
};
double d1, c, d2, p; //总距离,容量,路程/油量,起始油价
int n; // 加油站数量
point a[maxn]; // 存储所有加油站信息(包括起点)
int main(){
//输入信息
cin >> d1 >> c >> d2 >> p >> n;
a[0].pos = 0; a[0].price = p;
for(int i=1; i<=n; i++){
cin >> a[i].pos >> a[i].price;
}
//检查是否能到终点
int dmax = c*d2; //满油可走路程
if(d1 - a[n].pos > dmax) {cout << "No Solution\n"; return 0;}
for(int i=1; i<=n; i++){
if(a[i].pos - a[i-1].pos){
if(a[i].pos - a[i-1].pos > dmax) {cout << "No Solution\n"; return 0;}
}
}
//能到终点,开始模拟过程
double rest = 0; //当前剩余油量
double val = 0; //用钱数
for(int i=0; i<=n; i++){
int tmp = -1; //用来存最大行驶路程dmax中比自己价格还小的加油站位置
if(i){
rest -= (a[i].pos - a[i-1].pos)/d2; // 如果当前不是起点,要考虑走到这里的耗油
}
for(int j=i+1; j<=n; j++){ // 遍历后续加油站,找pos
if(dmax >= a[j].pos - a[i].pos && a[j].price < a[i].price){
tmp = j;
break;
}
}
if(tmp != -1){ // 如果范围内有更便宜的加油站,考虑到这个加油站就换油
double dx = a[tmp].pos - a[i].pos; // 从起点到那里的距离
double costOil = dx/d2; // 对应需要的油量
if(rest < costOil){ // 如果油不够,就加上到那里需要的油,如果够了就算了
val += (costOil - rest) * a[i].price;
rest += (costOil - rest); //加上这部分缺失的油
}
}
else{ // 如果范围内没有更便宜的加油站
double dx = d1 - a[i].pos; //到终点的距离
if(dmax < dx){ // 假如不能到终点,直接加满
val += (c-rest)*a[i].price; //加满需要的钱
rest = c; //加满
}
else{ // 能到终点,这个时候加到刚好能到终点的油就可以了
val += (dx/d2 - rest)*a[i].price;
printf("%.2f\n", val);
return 0;
}
}
}
}
补充:队友写的代码,感觉他的思路更好
他的思路是,首先不看后续加油,只看这次能不能到终点,记录不考虑加油情况下应该走的距离。之后再看有没有更好的加油站,如果有,就修改应该走的距离,使得当前油能正好到达
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct point
{
double d, p;
};
int cmp(point a, point b)
{
return a.d < b.d;
}
vector<point> v;
int main()
{
double D1, C, D2, P, N;
point temp;
cin >> D1 >> C >> D2 >> P >> N;
temp.d = 0;
temp.p = P;
v.push_back(temp);
temp.d = D1;
temp.p = 510;
v.push_back(temp);
for (int i = 0; i < N; i++)
{
cin >> temp.d >> temp.p;
v.push_back(temp);
}
sort(v.begin(), v.end(), cmp);
double maxd = C * D2;
double c = 0, cost = 0, d; // 当前剩余油量,用的钱,当前要走的距离
//检查是否能到终点
for (int i = 1; i < v.size(); i++)
if ((v[i].d - v[i - 1].d) > C * D2)
{
cout << "No Solution\n";
return 0;
}
//能到终点,开始模拟过程
for (int i = 0; i < v.size(); i++)
{
if (i) // 如果当前不是起点,要考虑走到这里的耗油
c -= (v[i].d - v[i - 1].d) / D2;
double p = v[i].p;
if (D1 - v[i].d >= maxd) // 检查当前能否到终点,如果不能到,d就是满油路程(d代表这次要走的距离)
d = maxd;
else // 如果能到终点,d就是到终点的距离
d = (D1 - v[i].d);
for (int j = i + 1; j < v.size(); j++)
{
if (v[j].d - v[i].d <= maxd) // 找范围内第一个更便宜的加油站
{
if (v[j].p < p)
{
d = v[j].d - v[i].d; // 更新路程
p = v[j].p; // 更新价格
break; //找到了就立刻终止
}
}
else // 超范围了,不能继续找后面的加油站
break;
}
if (d / D2 > c) // 当前油量不够,要加上一些油以到达下一个更好的加油站
{
cost += (d / D2 - c) * v[i].p;
c = d / D2;
}
}
printf("%.2lf\n", cost);
}