旅行家的预算(贪心+模拟)

这篇博客介绍了如何使用贪心算法解决一个旅行家的预算问题,即在有限的油量和预算下,找到最经济的加油策略到达目的地。博主分享了自己的解题思路,包括检查是否能到达终点,然后模拟加油过程,寻找更便宜的加油站。同时,博主还展示了队友的代码,指出其思路更为简洁。文章最后给出了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:

[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);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值