PAT甲级1033是一道复杂的贪心问题,写篇题解记录一下解题过程。这题的大致意思是从出发地A到目的地B中有若干个加油站,每个加油站都有自己的油价,怎么选择加油策略能使从A到B花费最少的价格。原题网址:PTA | 程序设计类实验辅助教学平台。
如何使用贪心策略解决这个问题呢?首先我们假设车辆来到了中间的某一加油站X,剩余油量是C,那么我们要做的是寻找X之后最近的油价低于X的加油站,如果存在该加油站Y,则需要判断X到Y是否能直接到达(也就是中间不再加油),如果可以那么在X把油量补充到能恰好到达Y的油量,如果不能则直接把油加满。
以上这一段就是贪心策略的关键,也就是尽可能保证车辆能够到达油价更低的加油站加油,但是如果油价更低的加油站距离太远无法直达,换句话说在车辆一箱油可达的路程里没有比当前加油站油价更低的加油站,那么就在当前加油站把油加满能获得最大的利益。当然还有一种情况是之后没有比当前加油站油价更低的加油站Y存在,那么当前加油站X就是之后所有加油站里油价最低的加油站,直接在此加油站把油加满就能获得最大利益。
如果要写代码的话,以上的叙述中还有许多细节需要注意,比如距离目的地够近的时候来到油价最低的加油站并不需要把油加满,而是加至能够恰好到达目的地的油量就可。详细的逻辑结构可以总结为:
##读入数据
##依次经过每一个车站
##判断当前油箱容量是否能直接到达目的地,若能则计算总价跳出循环
##如不能,寻找当前油站之后油价比当前油站更低的油站j
##j存在
##j是否能够在加满油的情况下直接到达,若能将油箱添加至刚好能到达j的油量
##若不能,则直接加满油箱
##j不存在
##判断满油是否能到达目的地,若能则将油箱加至恰好能到达目的地的油量
##如不能直接将油箱加满
##加油完毕,行驶到下一个加油站或终点
##最后一个加油站
##行驶到终点
##不是最后一个加油站
##可以行驶到下一个加油站
根据这种思路,写出代码:
##读入数据
import sys
class gas:
def __init__(self,price,distance):
self.price=float(price)
self.distance=float(distance)
line1=input().split()
cap=int(line1[0])
dis=int(line1[1])
avg=int(line1[2])
n=int(line1[3])
gases=[]
for i in range(n):
line2=input().split()
g=gas(line2[0],line2[1])
gases.append(g)
gases=sorted(gases,key=lambda gas:gas.distance)
if gases[0].distance!=0:
print("The maximum travel distance = 0.00")
sys.exit()
CurrentPrice=0
CurrentCap=0
##依次经过每一个车站
for i in range(n):
DisFromNowToFin = dis - gases[i].distance
##判断当前油箱容量是否能直接到达目的地,若能则计算总价跳出循环
if CurrentCap*avg>=DisFromNowToFin:
CurrentPrice=f"{CurrentPrice:.2f}"
print(CurrentPrice)
break
##如不能,寻找当前油站之后油价比当前油站更低的油站j
else:
j=-1
for k in range(i+1,n,1):
if gases[k].price<=gases[i].price:
j=k
break
##j存在
if j!=-1:
DisFromIToJ=gases[j].distance-gases[i].distance
CapFromIToJ = DisFromIToJ / avg
maxDis=avg*cap
##j是否能够在加满油的情况下直接到达,若能将油箱添加至刚好能到达j的油量
if DisFromIToJ<=maxDis:
if CapFromIToJ>CurrentCap:
Input=CapFromIToJ-CurrentCap
CurrentCap+=Input
CurrentPrice+=Input*gases[i].price
##若不能,则直接加满油箱
else:
Input=cap-CurrentCap
CurrentCap+=Input
CurrentPrice+=Input*gases[i].price
##j不存在
else:
DisFromIToFin=dis-gases[i].distance
CapFromIToFin=DisFromIToFin/avg
maxDis = avg * cap
##判断满油是否能到达目的地,若能则将油箱加至恰好能到达目的地的油量
if DisFromIToFin<=maxDis:
if CapFromIToFin>CurrentCap:
Input=CapFromIToFin-CurrentCap
CurrentCap+=Input
CurrentPrice+=Input*gases[i].price
##如不能直接将油箱加满
else:
Input=cap-CurrentCap
CurrentCap+=Input
CurrentPrice+=Input*gases[i].price
##行驶到下一个加油站或终点
if i==n-1:
##最后一个加油站
DisFromIToNext=dis-gases[i].distance
MaxCapDis = CurrentCap * avg
if DisFromIToNext>MaxCapDis:
##无法行驶到终点
maxDis=gases[i].distance+MaxCapDis
maxDis=f"{maxDis:.2f}"
print(f"The maximum travel distance = {maxDis}")
break
else:
##行驶到终点
CurrentPrice = f"{CurrentPrice:.2f}"
print(CurrentPrice)
break
else:
##不是最后一个加油站
DisFromIToNext=gases[i+1].distance-gases[i].distance
MaxCapDis=CurrentCap*avg
if DisFromIToNext>MaxCapDis:
##无法行驶到下一个加油站
maxDis=gases[i].distance+MaxCapDis
maxDis=f"{maxDis:.2f}"
print(f"The maximum travel distance = {maxDis}")
break
else:
##可以行驶到下一个加油站
CurrentCap-=DisFromIToNext/avg
写完这题之后我觉得代码还有很多优化空间,比如不需要把加油站和终点区别对待,而是把终点看做一个油价为0的加油站,有时间可以继续优化。