贪心算法解决To Fill or Not to Fill

贪心算法解决To Fill or Not to Fill

题目概述

With highways available, driving a car from Hangzhou to any other city is easy. But since the tank capacity of a car is limited, we have to find gas stations on the way from time to time. Different gas station may give different price. You are asked to carefully design the cheapest route to go.
Each input file contains one test case. For each case, the first line contains 4 positive numbers: Cmax (<= 100), the maximum capacity of the tank; D (<=30000), the distance between Hangzhou and the destination city; Davg (<=20), the average distance per unit gas that the car can run; and N (<= 500), the total number of gas stations. Then N lines follow, each contains a pair of non-negative numbers: Pi, the unit gas price, and Di (<=D), the distance between this station and Hangzhou, for i=1,…N. All the numbers in a line are separated by a space.
For each test case, print the cheapest price in a line, accurate up to 2 decimal places. It is assumed that the tank is empty at the beginning. If it is impossible to reach the destination, print “The maximum travel distance = X” where X is the maximum possible distance the car can run, accurate up to 2 decimal places.
题目大意:
现需要从杭州出发沿高速公路前往某目的地,已知车油箱最大容量、总距离、每单位车油走的距离和沿途的加油站总数,并给出每个加油站的单位油价和离杭州(出发地)的距离,让你计算出到达目的地可能的最小花费,若不可能到达,需要给出能够到达的最大距离。输出要求具体见题干(原题即为英文题)。

算法分析

本题本质上是特定情境下的价格最优化的问题,本来车的油箱是空的,所以车这一路上的车油必须从沿途的加油站获得,当然即便原来有油我们也可以通过改变最大容量转化为等价的问题,我们需要找出使得花费最小的策略。考虑这样的一般情形:我们到达了某一个加油站,但这时无法不经过别的加油站直接到达终点,且加满油可覆盖的最大里程内有若干加油站,此时如何从中选择下面将要前往的加油站?有下面两种可能性:
1.在这些加油站中没有比当前加油站的油价更低的,需要清楚的是我们必须在这些最大车程能覆盖到的加油站里选择一个前往加油,否则是不够支持我们到达目的地的,那么,当然要选择这其中价格最便宜的。因此针对这种情况,我们应当在当前的加油站把油加满,然后前往上述价格最便宜的加油站;
2.在这些加油站中存在比当前加油站的油价更低的,假设这样的加油站存在多个,我们应当选择距离最近的一个前往,因为我们不必把油加满,而是加到刚好够到达下一个加油站就可以了,毕竟更便宜嘛,当然如果不加油足够到达的话在当前加油站就不用做任何操作,这样一来优先选择最近的价格更低的加油站在同样的车途中总可以使开销得到降低,读者可以停下来仔细思考一下其中的道理。
以上就介绍完了贪心思想的主体部分,为了完整地解决问题我们还需要考虑如下几个特殊情形:
1.起点没有加油站,这样车走不了一点,返回最大距离0.00;
2.两个加油站距离太远,即便在当前加油站把油加满也到达不了,返回最大距离:当前行驶距离+油加满能行驶的最大距离;
3.终点如何处理?可以看到我们算法的主体部分都在讨论加油站的事情,这时候就需要我们开创性地把终点理解为一个油价为0的车站,其离出发地的距离就是总距离。
此外,为了提高算法效率,我们需要先按距离远近由近到远排序各加油站(包括终点,但您会发现由于终点的特性,它永远会被排在最后一个),难免会遇到两个加油站距离相等的情况,这时候要按油价从低到高排序(这样会避免某些无效操作)。

代码实现

经过一个下午的鏖战,代码总算是写出来了,读者可以先根据上面的分析自己尝试编写,再参考在下的拙迹:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
struct station{
	double price;
	double dis;
};
vector<station> sta;
bool cmp(station a,station b){
	if(a.dis==b.dis) return a.price<b.price;
	else return a.dis<b.dis;
}
int main(){
	double cMax,dTotal,dPergas;
	int n;
	while(scanf("%lf%lf%lf%d",&cMax,&dTotal,&dPergas,&n)!=EOF){
		sta.clear();
		station temp = {0.0,dTotal};
		//printf("%.2f %.2f\n",temp.price,temp.dis);
		sta.push_back(temp);
		for(int i=1;i<=n;i++){
			scanf("%lf%lf",&(temp.price),&(temp.dis));
			sta.push_back(temp);
		}
		//sort可保证终点排在最后 
		sort(sta.begin(),sta.end(),cmp);
		double nowDis=0.0,nowPrice=0.0,fuel=0.0;
		double maxRun = cMax*dPergas;
		int cheapest,nearest;
		for(int i=0;i<n;i++){
			//一开始忘记这步处理,但是多点测试通过了,纯属运气好 
			if(sta[i]!=nowDis)printf("The maximum travel distance = %.2f\n",nowDis);
			if((sta[i+1].dis-sta[i].dis)>maxRun){
				printf("The maximum travel distance = %.2f\n",nowDis+maxRun);
				break;
			}
			cheapest = nearest = i+1;
			int flag = 0;
			for(int j=i+1;j<=n&&(sta[j].dis-sta[i].dis)<=maxRun;j++){
				if(sta[j].price<sta[i].price){
					nearest = j;
					flag = 1;
					break;
				}
			}
			if(flag==0){//合法距离的加油站中,没有比当前价格更便宜的
				//找到最便宜的加油站 
				for(int j=i+1;j<=n&&(sta[j].dis-sta[i].dis)<=maxRun;j++){
					if(sta[cheapest].price>sta[j].price){
						cheapest = j;
					}
				}
				//把油加满,去往
				nowPrice += (cMax - fuel) * sta[i].price;
				fuel = cMax - (sta[cheapest].dis - sta[i].dis)/dPergas;
				nowDis = sta[cheapest].dis;
				i = cheapest - 1;
			}else{
				//找到了更便宜的加油站
				//最最便宜的加油站便是终点 
				double fuelReq = (sta[nearest].dis-sta[i].dis)/dPergas;
				if(fuelReq>fuel){
					nowPrice += (fuelReq - fuel) * sta[i].price;
					fuel = fuelReq;
				}
				fuel -= fuelReq;
				nowDis = sta[nearest].dis;
				i = nearest - 1;
			}
			//printf("flag:%d,nowPrice:%.2f,nowDis:%.2f,i:%d\n",flag,nowPrice,nowDis,i);
		}
		if(nowDis==dTotal)printf("%.2f\n",nowPrice);
	}
	return 0;
}

问题与总结

贪心算法是一类比较灵活的算法,记得之前上算法课的时候,老师讲过两个比较经典的题型:背包问题和驳船装货问题。但最近笔者在准备PAT考试的刷题过程中,发现可以用贪心算法解决的问题远不局限于此,也练到了不少好题,但这道让我受的磨折最为深刻,所以决定写这篇博客记录一下解题历程。
说到问题,这次可大有可说,to be honest,第一次写成的时候,我的算法思路是清晰正确的,但是运行时还是各种错,按照时间顺序,我遇到的问题见下:
1.无论输入数据是什么都输出The maximum travel distance = 0.00。要知道你检查了数遍算法发现逻辑没有一丁点毛病,但结果每回都是令人绝望的0.00。其原因后来灵光一现发现自己在接受双精度浮点型输入的时候没有使用**lf!!**咱就是说,还整啥scanf啊?C++的cin不香吗T^T
2.测试数据输出的结果是81,正确答案为82.89,再次说明本人的算法是没有毛病的,但是这代码要想跑通还真是命途多舛,我当时刚解决完上一个问题,明媚的心情立刻被这个bug弄得愁云密布,然后我出去散了会步,突然领会到,这个答案是整数相除产生的bug!然后我产生了一个令我自己都大跌眼镜的猜想:难道两个小数部分相同的浮点数相减,会自动变成一个整型数吗?

我立刻打开百度搜索,发现有人在php编程的时候会遇到类似的情况,在咖啡店喝完咖啡,我立刻飞奔回家想要看看是不是C编程真的存在这样惊世骇俗的Bug,然后我发现自己用int型变量接受了浮点数相减结果的事实,沉默不过3秒,我默默把int改回了double,输入测试数据得到正确答案82.89,而我由于被自己蠢到甚至没有任何情绪波动:P
3.虽然多点测试通过了,但之后复盘的时候发现自己原来的代码并不能解决起点没有加油站这种情况,经过思索后在while循环了里加上对于nowDis是否等于当前加油站距起点距离的判断,如果不存在这种特殊情况这一判断是显然成立的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值