题146.pat甲级练习-1033 To Fill or Not to Fill (25 分)


题146.pat甲级练习-1033 To Fill or Not to Fill (25 分)


一、题目

在这里插入图片描述
在这里插入图片描述

二、题解

本题要你求从开始位置0(注意不一定一开始就是在加油站,得看后面怎么输入的)到指定距离的最少加油钱总和。我们可以采用贪心的策略,由局部最优得到整体最优。大致思路如下:
1.开始我们先把输入的加油站装成结构体数组,然后按照距离和加油钱从小到大排序。
2.每次,我们站在当前位置上预判下一站的位置,这取决于我耗光整箱油能走的最大距离,在这个最大距离的范围内我去看有没有一个加油站的加油钱比当前站点的加油钱来的更小,找到一个就直接加对应距离的油到那里就好了(这里我当时有段时间转不过来,为什么直接找到一个就好了,万一后面还有可达的站点加油钱更少怎么办?答案是因为1.假设你要是有一个离当前站点近的且加油钱更少的站点A我肯定得直接到那A再去加油啊,然后要是后面有加油钱更少的站点B也是用从A那里花了更少的加油钱加油再往后走啊,这比你花更大的加油钱到达B可花钱少多了2.或者把最短路算法那套松弛理论搬过来理解也可,相当于用A对原点到B做了一个松弛),要是没有就直接用当前站点加满油,因为当前站点已经是范围内最小的加油钱了
3.不断循环2操作直到在正常情况下到达目的地就终止循环。
4.根据上述策略,可以将目的地也看做加油站,其距离设置为相应距离,油钱为0,这样其油钱一定比所有的加油站油钱都要低,在最后一定会选择目的地作为下一到达加油站。
5.几个注意点:(1)有可能当前加油站最近的一个加油站超过了车能行驶的最大距离,也就是最大的油量*每unit行驶路程,则无法到达目的地,最长距离就是当前站点+车能走的最大距离。(2)每次加油的时候记得考虑上次剩下的油,因为你有可能为了到达下一个油钱最小的站点根本就不用在当前站点加油直接用剩下的油就能到了或者是加了部分油就能刚好到了等等

#include <bits/stdc++.h>

using namespace std;

struct Station
{
    double cost;
    double d;
}S[502];

bool mycmp(struct Station s1,struct Station s2)
{
    if(s1.d!=s2.d)
    {
        return s1.d<s2.d;
    }
    else
    {
        return s1.cost<s2.cost;
    }
}

int main()
{
    int cmax,d,davg,n;
    cin>>cmax>>d>>davg>>n;
    for(int i=0;i<n;i++)
    {
        scanf("%lf%lf",&S[i].cost,&S[i].d);
    }
    S[n].d=d;
    S[n].cost=0;
    stable_sort(S,S+n+1,mycmp);//按从站点到起点的距离大小为第一关键字,加油钱为第二关键字非递降排序
    if(S[0].d!=0)//要是开始位置不在加油站直接g
    {
        printf("The maximum travel distance = 0.00\n");
    }
    else
    {
        int nowindex=0;//记录当前站点
        double nowcost=0;//记录当前花费
        double dmax=cmax*davg;//每次能预判的最大范围
        double c=0;//剩余油量
        int flag=1;
        while(nowindex<n)
        {
            int nextindex0=nowindex,nextindex1=nowindex;//前者为比当前站点加油钱少的站点,后者为后面的站点中加油钱最小的站点
            //预判处理
            for(int i=nowindex+1;i<=n;i++)//寻找最小花费的可达站点
            {
                if(S[i].d<=S[nowindex].d+dmax)
                {
                    if(S[i].cost<S[nowindex].cost)//只要后头有一个能到的站点的加油钱比当前加油钱少就停下
                    {
                        nextindex0=i;
                        break;
                    }
                    else if(nextindex1==nowindex||S[i].cost<=S[nextindex1].cost)//这个if用来找途经的花费最小的站点,这是为了使下一次加油钱最小,即使这个点的加油钱还是比当前站点的钱多
                    {
                        nextindex1=i;
                    }
                }
                else
                {
                    break;
                }
            }
            //当前处理
            if(nextindex0!=nowindex)//优先考虑比当前站点花费更少的可达站点
            {
                //加油
                if(S[nextindex0].d-S[nowindex].d-c*davg>0)//注意应该先考虑上次剩下的油够不够用了
                {
                    //不够用加油
                    nowcost+=(S[nextindex0].d-S[nowindex].d-c*davg)/davg*S[nowindex].cost;
                    //加油
                    c+=(S[nextindex0].d-S[nowindex].d-c*davg)/davg;//加油量记得扣去原先剩下的油!
                    //耗油到下一个站点
                    c-=(S[nextindex0].d-S[nowindex].d)/davg;//耗油量是从当前站点到下一个站点走的距离花的油量
                }
                else
                {
                    //够用不加油
                    c-=(S[nextindex0].d-S[nowindex].d)/davg;
                }
                nowindex=nextindex0;
            }
            else if(nextindex1!=nowindex)//次优先考虑可达的后面站点里加油钱最小的站点,此时当前位置加油钱为最少花费,加满
            {
                //加油
                nowcost+=(cmax-c)*S[nowindex].cost;
                c=cmax;
                //耗油到下一个站点
                c-=(S[nextindex1].d-S[nowindex].d)/davg;
                nowindex=nextindex1;
            }
            else//最远可达还是当前位置则直接gg(花了整箱油,你连最近的加油站都到不了,你还想干啥)
            {
                flag=0;
                break;
            }
        }
        if(flag==0)
        {
            printf("The maximum travel distance = %.2lf\n",S[nowindex].d+dmax);
        }
        else
        {
            printf("%.2lf",nowcost);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值