1

题目大意:

已知起点与终点的距离为D,油箱的最大油量为Cmax,单位汽油能够支持前进Davg。给定N个加油站的单位油价和离起点的距离C所有加油站都在一条线上),汽车初始时刻处于起点位置,油箱为空,且可以在任意加油站购买任意量的汽油(前提是不超过油箱容量),求从起点到终点的最小花费。如果无法到达终点,则输出能够行驶的最远距离。

算法思路:

此题考察的是贪心算法的思想,这里主要说明加油站的选择策略,并且提供2中不同的实现代码。对于每一个加油站,假设当前加油站为j,汽车可以行驶
的最大距离为max_dis。我们首先在从加油站j可以到达的最远加油站(距杭州的距离小于等于加油站j加上max_dis)之间进行遍历,如果可以找到比加油站j油价低的加油站,那么选择距离j最近的加油站为下一站。如果没有,那么找到油价最低的加油站作为下一站。如果从加油站j没有可以到达的加油站,那么说明没法前进,当前加油站的距离加上max_dis就是最大行驶距离.

算法实现1:

使用stations数组存储所有的加油站,这里将终点也作为一个加油站,其油价为0,距离为D,油价之所以为0,是因为当存在max_dis的范围包含终点的时候得选择油价比当前加油站j的油价低的加油站,不然直接去终点,所以0可以保证一定存在这样的加油站。然后按照加油站的距离从小到大进行排序,对于第一个加油站的距离不为0的得特殊判断,说明不可达,直接输出"The maximum travel distance = 0.00"。然后遍历每一个加油站(真正的加油站,所以i<N而不是N+1),首先找到下一站的地址min_pos,如果min_pos==-1,说明不存在下一站,输出最长行驶距离。否则,进行加油,如果当前的加油站j油价较低,那么在当前加油站加满,否则只加恰好到下一站的油。最后更新当前加油站的位置j=min_pos

算法实现2:

相比较于算法实现1,没有将终点当成是一个加油站,而是进行了特殊处理。这里设置了当前加油站为now,假设在now加满油后,可以离开加油站的最大距离为tempDis = Cmax*Davg(600),那么距离杭州的最大距离为gap = station[now].dis+tempDis。在下一站加油站的选取的实现上也有所区别,这里先获得汽车第一个达不到的加油站为i,那么now+1~i-1的加油站就是所有可以到达的,在now+1~i-1中选出下一站的位置。在now==N-1,也就是到达最后一个加油站的情况在最后一个加油站如果最远行驶地方可以到达终点,就累加跑到终点的油费,如果不能,说明无法到达终点,最远距离就是gap,并且让能否跑到终点的标志flag=false;同时如果当前不是最后一个加油站,会出现一种特判情况,就是当前加油站加满油后可以行驶的最远位置距起点的距离gap>=D,那么得判断在后面所有的加油站中是否存在比当前now加油站更加便宜的加油站temp,如果有,得先到temp然后再继续判断,如果没有直接到达终点,更新相关信息即可。

注意点:

1、所有的数据除了加油站数目是整型int,其他的都是double。
2、在起点没有加油站得特判,测试点2考察

提交结果:

第一次测试:测试点4错误。
错误原因未知,换了一种写法就通过了
对于算法实现1和2都通过PAT的测试
截屏2020-10-14 下午2.14.02.png
这里同时给上牛客网的测试结果,算法实现1通过了牛客网的测试,算法实现2只通过2个点((╥╯^╰╥))
截屏2020-10-14 下午2.15.32.png
截屏2020-10-14 下午2.22.24.png

测试用例:

Sample Input:
15 2383 13 10
424238335 0
86 60
49 161
62 1033
90 1279
63 1891
40 311
72 13
11 1945
67 1095
Sample Output:
The maximum travel distance = 506.00

AC代码1:

#include <cstdio>
#include <algorithm>

using namespace std;

struct Station{
    double price;//汽油单价
    double dis;//当前加油站与杭州的距离
};

bool cmpByDis(Station a,Station b){
    return a.dis<b.dis;
}

int main(){
    double Cmax,D,Davg;// 油箱容量,全长距离,每单位汽油行驶的距离
    int N; //加油站个数
    scanf("%lf %lf %lf %d",&Cmax,&D,&Davg,&N);
    Station stations[N+1];
    for (int i = 0; i < N; ++i) {
        scanf("%lf %lf",&stations[i].price,&stations[i].dis);
    }
    // 按照距离杭州的距离进行排序
    sort(stations,stations+N,cmpByDis);
    // 把终点也看成是一个加油站
    stations[N].dis = D;
    stations[N].price = 0;
    // 初始化数据
    double total_price = 0;//到达终点的总耗费
    double tank_capacity = 0;//当前的油箱油量
    double max_dis = Cmax*Davg;//汽车可以行驶的最大距离
    // 首先判断在起点是否有加油站
    if(stations[0].dis!=0){
        printf("The maximum travel distance = 0.00");
        return 0;
    }
    for (int j = 0; j < N; ) {
        // 找到在距离当前加油站max_dis内的所有加油站中油价比当前加油站低且最近的加油站,如果没有找到油价最低的加油站
        int min_pos = -1;
        double min_price = 10000000;
        for (int i=j+1;i<N+1&&stations[j].dis+max_dis>=stations[i].dis;++i) {
            if(min_price>stations[i].price){
                // 更新最小油价和位置
                min_pos = i;
                min_price = stations[i].price;
                if(min_price<stations[j].price){
                    // 找到比当前加油站油价更低的加油站i
                    break;
                }
            }
        }
        if (min_pos==-1){
            // 不存在可以到达的加油站
            printf("The maximum travel distance = %.2lf",stations[j].dis+max_dis);
            return 0;
        }
        // 将汽车开到min_pos位置的加油站
        // 首先计算所需要的油量
        double need = (stations[min_pos].dis-stations[j].dis)/Davg;
        if (stations[min_pos].price>stations[j].price){
            //当前的加油站油价更低,加满油再走
            total_price += (Cmax-tank_capacity)*stations[j].price;//更新耗费
            tank_capacity = Cmax;//加油完毕,更新油量
        }else {
            // 当前的加油站油价更高,只加恰好到min_pos的油
            if(tank_capacity<need){
                // 油箱的油不足,需要在当前加油站加油
                total_price += (need-tank_capacity)*stations[j].price;//更新耗费
                tank_capacity = need;//加油完毕,更新油量
            }
        }
        tank_capacity -= need;//行驶完毕,更新油量
        j = min_pos;//到达目的地,更新加油站位置
    }
    printf("%.2lf",total_price);
    return 0;
}

AC代码2:

#include<cstdio>
#include<algorithm>

using namespace std;

struct gasStation{
    float price;//油价 
    float dis;//加油站到杭州的距离 
}station[500];

bool cmp(gasStation g1,gasStation g2){
    return g1.dis<g2.dis;
}

int main(){
    float Cmax,D,Davg; //汽车油箱的量,杭州到终点城市的距离,每单元汽油可以跑的距离
    int N;//加油站数目
    scanf("%f %f %f %d",&Cmax,&D,&Davg,&N);
    for(int i=0;i<N;++i){
        scanf("%f %f",&station[i].price,&station[i].dis);
    } 
    sort(station,station+N,cmp);
    float cheapest = 0;//跑到终点的最少油钱
    bool flag = true;//能否跑到终点
    float maxDis = 0;//不能跑到终点的最大距离
    float tempDis = Cmax*Davg;//从当前加油站出发加满油能跑得最大距离
    int now = 0;//最近时间一次汽车加油的加油站
    float CRemain = 0;//油箱剩余油量,初始为0
    if(station[now].dis!=0){//当前开始的加油站距离杭州不为0,所以加不了油,无法发车 
        printf("The maximum travel distance = 0.00");
        return 0;
    }
    while(now<N){
        int CNeed = Cmax-CRemain;//当前汽车所能加油的最大油量
        float gap = station[now].dis+tempDis;//假设加满从当前加油站跑得最远的地方距离杭州的距离
        if(now==N-1){//当前是最后一个加油站,后面没有加油站了,就无法再遍历,得特判 
            if(gap<D) {//gap<D,说明没有跑到最终位置
                maxDis = gap;//能跑得最远距离就是gap
                flag = false;
            }else{//可以跑到终点 
                cheapest += station[now].price*(D-station[now].dis)/Davg;//累计跑到终点的钱
            }
            break; 
        }
        if(gap>=D){//从now加满油后可以跑到终点,且当前加油站不是最后一个加油站,得判断在now到最后加油站之间是否有比now更便宜的加油站
            int temp;
            for(temp=now+1;temp<N;++temp){
                if(station[temp].price<station[now].price){
                    break;
                }
            }
            if(temp!=N){//说明确实有,不能直接从now到终点,得先去k加油站,然后再判断
                float consumed_gas = (station[temp].dis-station[now].dis)/Davg;//从now到temp所需要加的油
                if(CRemain>=consumed_gas){//油箱剩余油量可以到达下一加油站,就不在now加油了 
                    CRemain -= consumed_gas;
                } else{//不够就加恰好到temp的油 
                    cheapest += station[now].price*(consumed_gas-CRemain);
                    CRemain = 0;//到达temp加油站的时候恰好为0
                }
                now = temp;//更新下一个达到的加油站 
                maxDis = station[temp].dis;//更新距离,跑到加油站temp
                continue;//直接进入下一次判断了,无需再找到比now高的最低油价,因为没有比now低油价的加油站直接就开到终点了 
            }else{//如果没有比now更低油价的加油站,直接去终点 
                cheapest += station[now].price*(D-station[now].dis)/Davg;//累计跑到终点的钱
                break;
            }
        }
        int i;//保存从now加满油后,当前汽车第一个跑不到的加油站i
        for(i=now+1;i<N;++i){//找到当前汽车第一个跑不到的加油站i,i-1就是所能跑到的最后一个加油站 
            if(station[i].dis>gap){
                break;
            }
        } 
        //now+1到i-1的所有加油站就是当前汽车所能跑到的所有加油站,找到价格最近的且油价小于now的那个加油站k
        int k;
        for(k=now+1;k<i;++k){
            if(station[k].price<station[now].price){
                break;
            }
        } 
        if(k==i){//说明没有比now更低油价的加油站,则找油价最低的
             k=now+1;//对k重新赋值 
            for(int j=now+2;j<i;++j){
                if(station[j].price<station[k].price){
                    k = j;
                }
            }
        }
        //在有比now加油站更低油价的时候,k保存的就是比now更低油价的加油站,如果没有k保存的就是最低油价的加油站
        if(station[k].price<station[now].price){//如果下一个加油站的油更便宜,只加到恰好到k加油站的油即可
            float consumed_gas = (station[k].dis-station[now].dis)/Davg;//从now到k所需要加的油
            if(CRemain>=consumed_gas){//油箱剩余油量可以到达下一加油站,就不在now加油了 
                CRemain -= consumed_gas;
            } else{//不够就加恰好到k的油 
                cheapest += station[now].price*(consumed_gas-CRemain);
                CRemain = 0;//到达k加油站的时候恰好为0
            }
        } else{//否则就加满 
            cheapest += station[now].price*CNeed;//累计加满油的钱
            CRemain = Cmax-(station[k].dis-station[now].dis)/Davg;//更新到达下一个加油站的剩余油量
        } 
        now = k;//更新为下一个加油站k 
        maxDis = station[k].dis;//更新距离,跑到加油站k
    }
    if(flag){//能跑到终点 
        printf("%.2f",cheapest);
    }else{
        printf("The maximum travel distance = %.2f",maxDis);
    }
    return 0;
}

乔梓鑫
569 声望17 粉丝

主要分享个人学习经验和心得