PAT 甲级(Python) #1033 To Fill or Not to Fill (25 分)贪心算法和分治法的python实现

#1033 To Fill or Not to Fill (25 分)
KY155 To Fill or Not to Fill
在这里插入图片描述在这里插入图片描述在这里插入图片描述

分治法求解(不完善):

#include <iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
double get_cost(int left,int right,double lef_bef,double rig_aft);
double get_cost_2Station(int left,int right,double lef_bef,double rig_aft);
int find_cheapest(int left,int right);
typedef struct Station{
    double price;
    double distance;
    double interval;
    //double before,after;
}Station;
Station st[510];
bool cmp(Station a,Station b);
int N;
double Cmax,Dmax,Davg;
const double eps=1.0e-8;
int main()
{

    scanf("%lf%lf%lf%d",&Cmax,&Dmax,&Davg,&N);
    //printf("\n\n\n    %lf %lf %lf %d  ",Cmax,Dmax,Davg,N);
    for(int i=1;i<=N;i++){
        scanf("%lf%lf",&st[i].price,&st[i].distance);
    }
    st[N+1].price=1.0e+100;//将终点也看作是一个加油站
    st[N+1].distance=Dmax;
    sort(st+1,st+2+N,cmp);
    st[1].interval=st[1].distance;
    for(int i=2;i<=N+1;i++){
        st[i].interval=st[i].distance-st[i-1].distance;
    }

    double efctDis=Cmax*Davg;
    if(st[1].distance>=eps){
        printf("The maximum travel distance = %.2lf",0.0);
        return 0;
    }

    for(int i=2;i<=N+1;i++){
        if(st[i].interval>=efctDis+eps){
            double _dis=st[i-1].distance+efctDis;
            printf("The maximum travel distance = %.2lf",_dis);
            return 0;
        }
    }
///
///
    double result=0.0;
    int _x,_y;
    double L_before, R_after;
   {
        //printf("%s\n","input the x and y: ");
       // printf("%s\n","input the L_before and R_after: ");
       // scanf("%d",&_x);
       // scanf("%d",&_y);
       // scanf("%lf",&L_before);
       // scanf("%lf",&R_after);
      //  if(_x>_y)continue;
      //  if(_y<1||_y>N+1)continue;
      //  if(_x<1||_x>N+1)continue;
        result=get_cost(1,N+1,0,0);//1,N+1;
        printf("%.2f",result);
    }

/
    return 0;
}
bool cmp(Station a,Station b){
    return a.distance<=b.distance;
}

double get_cost(int left,int right,double lef_bef,double rig_aft){
    if(left>=right){
        return 0.0;
    }

    if(left+1==right){
        return
            get_cost_2Station(left,right,lef_bef,rig_aft);
    }
    int chp_pos=find_cheapest(left,right-1);//cheapest station position;
    if(chp_pos==left){
        if(Cmax*Davg>=st[right].distance-st[left].distance&&rig_aft<=eps){
            //只需起始处加油
            double _cost=st[right].distance-st[left].distance;
            _cost=(rig_aft+_cost-lef_bef)*(st[left].price)/Davg;
            return _cost;
        }else{
            bool MustFull=false;
            if(Cmax*Davg<=st[right].distance-st[left].distance)
                MustFull=true;
            if(Cmax*Davg<=st[right].distance-st[left].distance+rig_aft&&
               rig_aft>=eps&&st[left].price<=st[right].price)
                MustFull=true;
            if(MustFull){
                //起始处需要加满;
                double Lcost=(Cmax*Davg-lef_bef)*(st[left].price)/Davg;
                int NewLeft=left+1;
                int NewRight=right;
                double NewLef_bef=Cmax*Davg-st[left+1].interval;
                double NewRig_aft=rig_aft;
                return
                    Lcost+get_cost(NewLeft,NewRight,NewLef_bef,NewRig_aft);
            }else{
                //起始处不需要加满;
                return
                    (st[right].distance-st[left].distance-lef_bef)*st[left].price/Davg+0.0
                    +rig_aft*st[right].price/Davg;
            }
        }
    }
    if(chp_pos==right-1){
        double cost_1=get_cost(left,chp_pos,lef_bef,0.0);
        double cost_2=st[right].interval*st[chp_pos].price/Davg;
        return cost_1+cost_2;
    }else{
        double this_sta_bef;
        double this_sta_aft;
        double duplicate_cost;
        if(lef_bef<=st[chp_pos].distance-st[left].distance){
            this_sta_bef=0.0;
        }else{
            this_sta_bef=
                lef_bef-(st[chp_pos].distance-st[left].distance);
        }

        if(Cmax*Davg>=st[right].distance-st[chp_pos].distance){
                this_sta_aft=st[right].distance-st[chp_pos].distance;
        }else{
                this_sta_aft=Cmax*Davg;
        }

        double cost_1=get_cost(left,chp_pos,lef_bef,this_sta_aft);
        double cost_2=get_cost(chp_pos,right,this_sta_bef,rig_aft);
        duplicate_cost=(this_sta_aft-this_sta_bef)/Davg*st[chp_pos].price;
        return cost_1+cost_2-duplicate_cost;

    }

}

int find_cheapest(int left,int right){
    //find the position of the cheapest station;
    if(left>right){
        return -1;
    }
    if(left==right){
        return left;
    }
    double cheap=st[left].price;
    int index=left;
    for(int i=left+1;i<=right;i++){
        if(cheap>=eps+st[i].price){
            cheap=st[i].price;
            index=i;
        }
    }
    return index;
}

double get_cost_2Station(int left,int right,double lef_bef,double rig_aft){
    //整个路程只经过两个加油站,并且终点也看做加油站;
    //在起始点加油站时的初始油量可以不为零为lef_bef
    //到达终点站加油站时的剩余油量可以不为零,为rig_aft
    if(left+1!=right){
        return 0.0;
    }
    if(st[left].price>=st[right].price){
    //右边便宜
        if(lef_bef>=st[right].interval){
            //可以不加油,直接开过来
            return
                (rig_aft-lef_bef+st[right].interval)
                *st[right].price/Davg;
        }else{
            //必须加油才能开过来;
            double cost_1=
                (st[right].interval-lef_bef)*st[left].price/Davg;
            double cost_2=rig_aft*st[right].price/Davg;
            return cost_1+cost_2;
        }
    }else{
    //左边便宜
        if(Cmax*Davg>=st[right].interval+rig_aft){
            //油箱可以不加满,而且右边加油站不用去加,只在左边加油;
            return
                (rig_aft+st[right].interval-lef_bef)
                *st[left].price/Davg;
        }else{
            //油箱需要加满,而且右边加油站也要去加;
            double cost_1=(Cmax*Davg-lef_bef)*st[left].price/Davg;
            double cost_2=
                (rig_aft-Cmax*Davg+st[right].interval)
                *st[right].price/Davg;
            return cost_1+cost_2;
        }
    }
}

测评结果:

在这里插入图片描述
贪心算法:

贪心算法:
思路:
考虑当前加油站的加油策略:
在加满一整箱油所能到达的范围内,尝试寻找最近的价格低于当前加油站价格的最近的加油站,
1:  能找到这样的加油站,那么在当前加油站加上恰好够到达此加油站的油,
    继续在下一个加油站在做加油策略的分析和决策.
2:  如果不能找到这样的加油站,
    (如果从当前加油站可以直接到达终点,那就不需要加满油,只需加到足够到终点的油即可).
    如果无法从当前加油站直接到达终点,那么在当前加油站加满油之后,
    前往在所能到达的范围内价格尽可能便宜的加油站,
    到达该加油站之后,继续在这个加油站做加油策略的分析和决策.
3:  如果起点位置没有加油站,即位置为0处没有加油站,那么无法出发,
    输出最大的行驶距离为0.00并退出程序.

python代码:

import collections
MaxPrice = 10**12  # 设最大油价
# 用于抽象加油站的信息
PetrolStation = collections.namedtuple('PetrolStation', ['price', 'distance'])
def getNextOptimalStationID(PetrolStationsList, current_station_idx, distance_tank, N):
    currentStation  = PetrolStationsList[current_station_idx]
    # maxRange 表示当前加油站加满之后可以最远到达的位置
    maxRange = currentStation.distance + distance_tank
    idx = current_station_idx + 1  ##########
    minPrice = MaxPrice  ############
    minPrice_idx = current_station_idx + 1  ############
    while idx <= N and PetrolStationsList[idx].distance <= maxRange:
        if PetrolStationsList[idx].price <= currentStation.price:
            return idx # 优先选择行程范围内最近的价格低于当前加油站的下一个加油站
        elif PetrolStationsList[idx].price < minPrice:
            minPrice_idx = idx
            minPrice = PetrolStationsList[idx].price
        idx += 1
    if idx == current_station_idx + 1:
        return -1  # 表示达不到下一加油站
    else:
        return minPrice_idx

def main():
    # 油箱容量, 总距离, 公里每升, 加油站总数
    Cmax, D, Davg, N = map(eval, input().strip().split())
    PetrolStationsList = list()  # 列表类型,用于保存N个加油站

    stationsDict = dict()
    for i in range(N):
        price, distance = map(eval, input().strip().split())  # 每个加油站的价格和到起点的距离
        if distance in stationsDict:
            if price < stationsDict[distance]:
                stationsDict[distance] = price
        else:
            stationsDict[distance] = price
    N = len(stationsDict)
    for distance, price in stationsDict.items():
        PetrolStationsList.append(PetrolStation(price,distance))  # 添加到列表
    PetrolStationsList.append(PetrolStation(MaxPrice, D))  # 终点也视作是一个加油站
    PetrolStationsList.sort(key=lambda x:x.distance)  # 加油站按从近到远排列
    distance_tank = Cmax * Davg  # 一整箱油加满后可以行驶的距离
    current_station_idx = 0  # 当前所在加油站
    cost, petrolRst = 0.0, 0.0  # 当前累计消费, 当前油箱剩余油量
    if PetrolStationsList[0].distance > 0.0:  # 表示起点处没有加油站,汽车无法启动,停在起点
        print("The maximum travel distance = {0:.2f}".format(0.0))
        return
    while current_station_idx < N:
        nextOptimal_idx = getNextOptimalStationID(PetrolStationsList, current_station_idx, distance_tank, N)
        if nextOptimal_idx == -1:  # 表示无法到达下一个加油站(或者终点)
            print("The maximum travel distance = {0:.2f}".format(
                PetrolStationsList[current_station_idx].distance + distance_tank))
            return
        elif nextOptimal_idx == N:  # 表示下一站直接到终点
            currentStation = PetrolStationsList[current_station_idx]
            cost = cost + \
                ((D - currentStation.distance)/Davg - petrolRst) * currentStation.price
            print("{0:.2f}".format(cost))
            return
        else:
            currentStation = PetrolStationsList[current_station_idx]
            nextStation = PetrolStationsList[nextOptimal_idx]
            need_petrol = (nextStation.distance-currentStation.distance)/Davg  # 开到下一站消耗的油量
            #######################################################
            if currentStation.price > nextStation.price:  # 下一站更便宜  
                if need_petrol > petrolRst:  # 油箱余量不够,需要加油
                    cost = cost + (((nextStation.distance-currentStation.distance)/Davg-petrolRst)*currentStation.price)
                    petrolRst = 0.0
                else:  # 油箱余量充足,不需要加油
                    petrolRst = petrolRst - need_petrol
            else:  # 当前站更便宜
                if currentStation.distance + distance_tank >= D:
                    cost = cost + (D - currentStation.distance) / Davg * currentStation.price
                    print("{0:.2f}".format(cost))
                    return
                cost += (Cmax-petrolRst)*currentStation.price
                petrolRst = Cmax - need_petrol
            current_station_idx = nextOptimal_idx

if __name__ == "__main__":
    try:
        while True:
            main()
    except:
        pass

测评结果:
在这里插入图片描述
在这里插入图片描述

方法二,利用分治法的python实现
算法的思路:

    '''
    运用分治法的思路:
    将问题转为: 
        从加油站i,到加油站j,
        到达i时,油量已知,加油量未知,即出发时的油量未知
        到达j时,油量未知,但从j再次出发时,油量已知
        getMinimalCost(PetrolStationsList,startSta_idx=0,startPetrol=0,endSta_idx=stationNum,endPetrol=0,kmPerLitre=kmPerLitre,Capacity=Capacity)
        参数说明:
            PetrolStationsList, 所有实际有效的加油站构成的列表,按距起始位置从近到远排列,同一个位置可能有多个加油站,此时只考虑价格最便宜的一个加油站
            startSta_idx=0,  汽车出发的加油站所在的索引位置
            startPetrol=0,  汽车刚到达起始加油站时在未进行加油时,油箱所剩燃油.
            endSta_idx=stationNum,  汽车到达终点加油站的索引位置,注意,终点视为一个油价无限大的加油站
            endPetrol=0,  汽车到达终点加油站之后,加完油(加油量可以是0)后油箱的油量.
            kmPerLitre=kmPerLitre, 消耗单位油量汽车可以行驶的距离
            Capacity=Capacity, 油箱容量
        分治法:
            找到区间之中最便宜的加油站,
            分3种情况,
                1. 区间最左端的是最便宜的加油站,分析该加油站需要加多少油,然后将问题的区间由(start,end)变为(start+1,end),问题规模减1,
                2. 区间最右端的是最便宜的加油站,分析该加油站需要加多少油,然后将问题的区间由(start,end)变为(start,end-1),问题规模减1,
                3. 最便宜的加油站optimal在区间内部,并且不在区间两端点.然后将问题的区间由(start,end)变为(start,optimal-1)和(optimal,end),问题规模减少,递归求解.

    '''
import collections
MaxPrice = 10**12  # 设最大油价
# 用于抽象加油站的信息
PetrolStation = collections.namedtuple('PetrolStation', ['price', 'distance'])

# 在索引为startSta_idx和endSta_idx的加油站之间(包括两端的两个加油站)寻找一个价格最便宜的加油站的索引位置
def findCheapestStation_idx(PetrolStationsList,startSta_idx,endSta_idx):
    minPrice_idx, minPrice = -1, MaxPrice 
    for idx in range(startSta_idx,endSta_idx+1):
        if minPrice >= PetrolStationsList[idx].price:
            minPrice, minPrice_idx = PetrolStationsList[idx].price, idx
    return minPrice_idx

# def getMinimalCostAdjacent(PetrolStationsList,startSta_idx,startPetrol,endSta_idx,endPetrol,kmPerLitre,Capacity):
#     startSta, endSta = PetrolStationsList[startSta_idx],PetrolStationsList[endSta_idx]
#     consumption = (endSta.distance - startSta.distance) / kmPerLitre
#     if startSta.price > endSta.price:
#         startAdd = max((consumption-startPetrol),0)
#         endAdd = endPetrol - startPetrol + consumption - startAdd
#         return startAdd * startSta.price + endAdd * endSta.price
#     else:
#         startAdd = min(consumption+endPetrol-startPetrol,Capacity-startPetrol)
#         endAdd = endPetrol - startPetrol + consumption - startAdd
#         return startAdd * startSta.price + endAdd * endSta.price

def getMinimalCost(PetrolStationsList,startSta_idx,startPetrol,endSta_idx,endPetrol,kmPerLitre,Capacity):
    if startSta_idx > endSta_idx:
        return -1  # -1表示出错
    if startSta_idx == endSta_idx:  # 递归终止条件
        cost = (endPetrol - startPetrol) * PetrolStationsList[startSta_idx].price
        return cost
    # elif startSta_idx == endSta_idx - 1:
    #     return getMinimalCostAdjacent(PetrolStationsList,startSta_idx,startPetrol,endSta_idx,endPetrol,kmPerLitre,Capacity)
    minPriceStation_idx = findCheapestStation_idx(PetrolStationsList,startSta_idx,endSta_idx)
    if endSta_idx != minPriceStation_idx != startSta_idx:
        consumption = (PetrolStationsList[minPriceStation_idx].distance - PetrolStationsList[startSta_idx].distance)/kmPerLitre
        minPriceStationStartPetrol = max(0,startPetrol-consumption)
        petrol = minPriceStationStartPetrol + (PetrolStationsList[minPriceStation_idx].distance - PetrolStationsList[minPriceStation_idx-1].distance) / kmPerLitre
        return \
            getMinimalCost(PetrolStationsList,startSta_idx,startPetrol,minPriceStation_idx-1,petrol,kmPerLitre,Capacity) +\
                getMinimalCost(PetrolStationsList,minPriceStation_idx,minPriceStationStartPetrol,endSta_idx,endPetrol,kmPerLitre,Capacity)
    elif endSta_idx == minPriceStation_idx:
        consumption = (PetrolStationsList[endSta_idx].distance - PetrolStationsList[startSta_idx].distance)/kmPerLitre
        min_Start_petrol = max(0, startPetrol-consumption)  # 
        petrol = min_Start_petrol + (PetrolStationsList[endSta_idx].distance - PetrolStationsList[endSta_idx-1].distance) / kmPerLitre
        return (endPetrol-min_Start_petrol) * PetrolStationsList[endSta_idx].price +\
            getMinimalCost(PetrolStationsList,startSta_idx,startPetrol,endSta_idx-1,petrol,kmPerLitre,Capacity)
    elif minPriceStation_idx == startSta_idx:
        consumption = (PetrolStationsList[endSta_idx].distance - PetrolStationsList[startSta_idx].distance)/kmPerLitre
        minStationEndPetrol = min(Capacity,consumption+endPetrol)
        petrol = minStationEndPetrol - (PetrolStationsList[startSta_idx+1].distance - PetrolStationsList[startSta_idx].distance)/kmPerLitre
        return \
            (minStationEndPetrol - startPetrol) * PetrolStationsList[startSta_idx].price +\
                getMinimalCost(PetrolStationsList,startSta_idx+1,petrol,endSta_idx,endPetrol,kmPerLitre,Capacity)

def main():
    # 油箱容量, 总距离, 公里每升, 加油站总数
    Capacity, DistanceTotal, kmPerLitre, stationNum = map(eval, input().strip().split())
    PetrolStationsList = list()  # 列表类型,用于保存N个加油站

    stationsDict = dict()
    for i in range(stationNum):
        price, distance = map(eval, input().strip().split())  # 每个加油站的价格和到起点的距离
        if distance in stationsDict:
            if price < stationsDict[distance]:
                stationsDict[distance] = price
        else:
            stationsDict[distance] = price
    stationNum = len(stationsDict)
    for distance, price in stationsDict.items():
        PetrolStationsList.append(PetrolStation(price,distance))  # 添加到列表
    PetrolStationsList.append(PetrolStation(MaxPrice, DistanceTotal))  # 终点也视作是一个加油站
    PetrolStationsList.sort(key=lambda x:x.distance)  # 加油站按从近到远排列
    distance_tank = Capacity * kmPerLitre  # 一整箱油加满后可以行驶的距离
    # 先判断是否能走到终点
    if PetrolStationsList[0].distance > 0.0:  # 表示起点处没有加油站,汽车无法从起点启动
        print("The maximum travel distance = {0:.2f}".format(0.0))
        return
    for i in range(stationNum):  # 把终点视为一个特殊的加油站,因此加油站实际数量为:(stationNum + 1)
        if PetrolStationsList[i].distance + distance_tank < PetrolStationsList[i+1].distance:
            print("The maximum travel distance = {0:.2f}".format(PetrolStationsList[i].distance + distance_tank))
            return
    
    # 剩余是可以到达终点的情形
    '''
    运用分治法的思路:
    将问题转为: 
        从加油站i,到加油站j,
        到达i时,油量已知,加油量未知,即出发时的油量未知
        到达j时,油量未知,但从j再次出发时,油量已知
        getMinimalCost(PetrolStationsList,startSta_idx=0,startPetrol=0,endSta_idx=stationNum,endPetrol=0,kmPerLitre=kmPerLitre,Capacity=Capacity)
        参数说明:
            PetrolStationsList, 所有实际有效的加油站构成的列表,按距起始位置从近到远排列,同一个位置可能有多个加油站,此时只考虑价格最便宜的一个加油站
            startSta_idx=0,  汽车出发的加油站所在的索引位置
            startPetrol=0,  汽车刚到达起始加油站时在未进行加油时,油箱所剩燃油.
            endSta_idx=stationNum,  汽车到达终点加油站的索引位置,注意,终点视为一个油价无限大的加油站
            endPetrol=0,  汽车到达终点加油站之后,加完油(加油量可以是0)后油箱的油量.
            kmPerLitre=kmPerLitre, 消耗单位油量汽车可以行驶的距离
            Capacity=Capacity, 油箱容量
        分治法:
            找到区间之中最便宜的加油站,
            分3种情况,
                1. 区间最左端的是最便宜的加油站,分析该加油站需要加多少油,然后将问题的区间由(start,end)变为(start+1,end),问题规模减1,
                2. 区间最右端的是最便宜的加油站,分析该加油站需要加多少油,然后将问题的区间由(start,end)变为(start,end-1),问题规模减1,
                3. 最便宜的加油站optimal在区间内部,并且不在区间两端点.然后将问题的区间由(start,end)变为(start,optimal-1)和(optimal,end),问题规模减少,递归求解.

    '''
    cost = getMinimalCost(PetrolStationsList,startSta_idx=0,startPetrol=0,endSta_idx=stationNum,endPetrol=0,kmPerLitre=kmPerLitre,Capacity=Capacity)
    print("{0:.2f}".format(cost))
    

if __name__ == "__main__":
    try:
        while True:
            main()
    except:
        pass

运行结果显示:

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值