#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
运行结果显示: