洛谷 P1016 [NOIP1999 提高组] 旅行家的预算【贪心】

原题链接:https://www.luogu.com.cn/problem/P1016

题目描述

一个旅行家想驾驶汽车以最少的费用从一个城市到另一个城市(假设出发时油箱是空的)。给定两个城市之间的距离 D1​、汽车油箱的容量 C(以升为单位)、每升汽油能行驶的距离 D2​、出发点每升汽油价格P和沿途油站数 N(N 可以为零),油站 i 离出发点的距离 Di​、每升汽油价格 Pi​(i=1,2,…,N)。计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution

输入格式

第一行,D1​,C,D2​,P,N。

接下来有 N 行。

第 i+1 行,两个数字,油站 i 离出发点的距离 Di​ 和每升汽油价格 Pi​。

输出格式

所需最小费用,计算结果四舍五入至小数点后两位。如果无法到达目的地,则输出 No Solution

输入输出样例

输入 #1

275.6 11.9 27.4 2.8 2
102.0 2.9
220.0 2.2

输出 #1

26.95

说明/提示

N≤6,其余数字 ≤500。

NOIP1999 普及组第三题、提高组第三题

解题思路:

这个题目我觉得是一个比较好的贪心题,这个题目可能思维不是那么的难,但是一写起来代码细节特别的多,一些边界哨兵的合理设置就能减少一些特判,就能减少代码量。

首先考虑一下简化版,首先我们认为汽车油箱的容量是无限的,那么这个题目就简单多了,每个位置城市肯定都是从当前位置前面所有的城市中油价最低的城市过来,这样就能保证总花费最少,但是这个题目难就难在汽车的容量是有限制的,这就相当于把这个题目加了一个限制,就是当前位置城市只能从当前位置城市前面一定区间内的城市转移过来,下面我们考虑一种贪心策略。

(1)在每一个位置,考虑在加满油时,当前位置城市后面可以到达的城市中找第一个比当前位置城市油价低的城市,如果找到了,将油加到刚好能够到达这个城市即可。

那么为什么加到能刚好到达下一个到达的城市就是最优的,因为我们找到的下一个城市油价更低,在当前位置多加一些油,不如到下一个城市在加,因为下一个城市花费更低。

(2)考虑在加满时,如果在当前位置城市后面可以到达的城市里面找不到油价更低的城市,那么我们需要在能够到达的城市里面找到一个油价最低的城市,并且此时我们应该加满油,然后到达这个油价最低的城市,那么此时又有一个疑问了,那么此时为什么要加满油呢。

此时为什么加满油最优?因为下一个要到达的城市油价比当前位置城市油价高,所以在当前位置加满油是更优的,那么为什么要加满呢,我就不能少加一点吗?,事实证明这是不可以的,我来画个图描述一下:

根据上述图中分析,可以知道这种情况加满油肯定是更优的。

(3)第三种情况就是,如果当前城市不是目标城市,并且就算加满油,但是连最近的城市都到不了,那么说明无法到达目标城市,表示无解。

细节分析见代码处。

时间复杂度:O(n),因为每一个位置只会枚举一次。

空间复杂度:O(n)。

cpp代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 10;

double dist, cap, travel_dist, oil_cost; // dist表示俩座城市的距离,cap表示汽车油箱的容量,travel_dist表示每升油能形式的距离,oil_cost表示起点每升油的价格
int n;                                   // n表示起点和终点之间有n座加油站
double d[N], oil[N];                     // d[i]表示第i座加油站距离起点的距离,oil[i]表示第i座加油站每升油的价格

int main()
{
    cin >> dist >> cap >> travel_dist >> oil_cost >> n;
    for (int i = 1; i <= n; i++)
        cin >> d[i] >> oil[i];
    d[0] = 0, oil[0] = oil_cost;  //起点哨兵记得标记好
    d[n + 1] = dist, oil[n + 1] = 0;  //目标城市为n+1,距离为dist,油价设为0,表示如果可以在前面分析的操作中可以直接到达目标城市,那么就直接到达

    double total_cost = 0;  //记录总的花费
    int curr_pos = 0;  //记录当前所在位置,初始时在位置0表示起始城市
    bool Solution = true;  //表示是否无法到达目标城市,为true表示可以到达目标城市,为false表示不能到达目标城市
    double remain_oil = 0;  //表示当前油箱剩余油量
    while (curr_pos < n + 1)  //只要没有到达目标城市n+1,那么就需要寻找下一个需要前往的城市
    {
        int next_pos = curr_pos + 1, min_pos = next_pos;
        if ((d[next_pos] - d[curr_pos]) / travel_dist - cap > 1e-4) //如果连最近的一个城市都无法到达,说明无解
        {
            Solution = false;
            break;
        }
        double min_cost = oil[next_pos]; //记录当前位置城市后面能到达的最小油价城市
        bool ok = true;
        for (int i = next_pos; i <= n + 1; i++)
        {
            double liter = (d[i] - d[curr_pos]) / travel_dist;
            if (liter - cap > 1e-4)  //当前位置在油箱加满油时都无法到达,后面的城市就都无法到达了,此时还没有找到第一个比当前位置城市油价低的城市,把ok设为false,表示后面的可以到达的城市没有比当前位置城市油价低的
            {
                ok = false;
                break;
            }
            if (min_cost - oil[i] > 1e-4)  //处理当前位置城市后面可以到达的城市里面油价最低的那个城市
            {
                min_pos = i;  //记录位置
                min_cost = oil[i];  //记录最小油价
            }
            if (oil[curr_pos] - oil[i] > 1e-4)
            {
                next_pos = i;   //当前位置城市后面可以到达的城市里面,找到了第一个比当前位置城市油价低的城市
                break;
            }
        }

        if (!ok)  //在当前位置城市后面可以到达的城市里面没有找到比当前位置城市油价低的城市
        {
            next_pos = min_pos;  //那么就应该去往后面可以到的城市里面油价最低的那个城市
        }
        double distance = d[next_pos] - d[curr_pos];
        double need_liter = distance / travel_dist;  //记录到达后面需要到达的位置需要花费多少升油
        if (!ok)  //在当前位置城市后面可以到达的城市中没有找到比当前位置油价更低的城市
        {
            double need_oil = cap - remain_oil;  //根据上面的分析,这种情况应该加满油,油箱中前面还剩余了remain_oil升油,那么需要加cap-remain_oil升油
            total_cost += need_oil * oil[curr_pos];  //在当前位置城市需要加need_oil升油,所以需要花费need_oil*oil[curr_pos]的钱
            remain_oil = cap - need_liter;  //油箱加满油之后,到达下一个城市需要花费need_liter升油,所以到达下一个位置next_pos之后,油箱中应该还剩下cap-need_liter升油
        }
        else //找到了
        {
            double need_oil = need_liter - remain_oil;  //找到了就只需要将油箱中的油加到刚好能到达下一个需要到达的城市即可
            total_cost += need_oil * oil[curr_pos];  //需要加need_oil升油,所以花费为need_oil*oil[curr_pos]
            remain_oil = 0;  //到达下一个需要到的城市之后,此时油箱中剩余的油量为0
        }

        curr_pos = next_pos;  //到达下一个需要到达的城市
    }
    if (!Solution)  //无解
    {
        cout << "No Solution";
    }
    else
    {
        printf("%.2lf", total_cost);  //输出最小花费
    }
    return 0;
}
  • 24
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值