PAT-A-1033 To Fill or Not to Fill (25 分)贪心算法 C++题解

53 篇文章 0 订阅
30 篇文章 1 订阅

1033 To Fill or Not to Fill (25 分)

题目传送门:1033 To Fill or Not to Fill (25 分)

一、题目大意

求从杭州站到目的站的最小油费。

杭州站与目的站之间有很多个加油站,每个加油站的油价不同。如果无法到达输出最大行驶距离,否则输出到达目的站的最小油费。

二、解题思路

这道题很明显是贪心算法,但是具体怎么求下一站点,还挺复杂的。

注意:题目中说所有的站点的坐标都是小于等于终点站的,那么我们就不用担心在中途会达到终点站,终点站一定是在最后一站后面。

先根据距离排序,如果第一个点的坐标不是0则无法加油出发,不可到达终点,直接输出0即可。

然后在while循环当前点开始分类贪心讨论:

  1. 如果从当前站点加满油的可达范围内,没有站点
    1.1 如果可以加满油的可达范围内到达终点,油不够则加油,结束循环
    1.2 无法到达终点,则直接输出最大可达距离,然后结束循环。
  2. 如果从当前站点加满油的可达范围内,有站点
    2.1 如果可达范围内有比当前站更便宜的站,先去最近的更便宜的站。
    2.2 如果可达范围内没有比当前站更便宜的站,则先看是否可以直接到达终点。如果无法到达终点,则把油加满,去往可达范围内最便宜的站。

这道题困惑了我好几天了,今天思路终于通透了,之前一直想不明白的是,如果到了没找到比当前便宜的加油站,而加满油去可达范围内最近的加油站,那去下一站时,下一站更便宜而原本又装了满油没用完,岂不是很浪费钱,感觉不符合贪心思想。但今天我终于想通了,就是满油到达最近的加油站后,是无法靠之前的油到更便宜下一站的,因为我是在可达范围内无更便宜加油站前提下进行的,所以我在此站装满油后,到了最便宜的站后,就是还剩了油, 也无法到达下一个更便宜的加油站。所以是符合贪心规则的。
还有一个疑问,就是会不会到达终点站的时候,还有剩余的油,如果有剩余的油,那岂不是就不是最优解了吗。不过这个问题在我写完这句话的时候,突然想通了,就是在上面分析2.1中做的处理逻辑,先判断是否可以到达终点,如果不可以到达终点,再加满油去最近的站点。

三、AC代码

#include<bits/stdc++.h>
using namespace std;
template<typename T = int>
T read(){
	T x;
	cin >> x;
	return x;
}
struct Station
{
	double price;
	int position;
};
int main(){
	double C = read<double>(), D = read<double>(), AVG = read<double>();
	int N = read();
	vector<Station>v(N);
	for(int i = 0; i < N; i++){
		v[i].price = read<double>();
		v[i].position = read<double>();
	}
	sort(v.begin(), v.end(), [](Station a, Station b)->bool{
		return a.position < b.position;
	});
	if(v[0].position > 0){
		cout << "The maximum travel distance = 0.00" << endl;
		return 0;
	}
	// for(auto i: v){
	// 	cerr << "jal::" << i.position << ' ' << i.price << endl;
	// }
	double fullDis = C * AVG;
	// cout << "fullDis = " << fullDis << endl;
	int curIndex = 0;
	double curPos, curPrice, sumPrice = 0, curCap = 0;
	while(curIndex < v.size()){
		 curPos = v[curIndex].position, curPrice = v[curIndex].price;
		 // cerr << "curIndex = " << curIndex << endl << endl;
		 int hasStation = -1, cheapestPos = -1, cheapestPos2 = -1;
		for(int i = curIndex+1; i < v.size(); i++){
			if(v[i].position > curPos + fullDis)break;
			hasStation = 1;// 范围内可达
			cheapestPos2 = i;
			if(v[i].price < curPrice){
				cheapestPos = i;
				break;// 最近的更便宜的站
			}
		}
		// cerr << "cheapestPos = "<< cheapestPos << "  cheapestPos2 = " << cheapestPos2 << endl;
		if(hasStation == 1){// 1. 范围内有可达站点
			if(cheapestPos != -1){// 1.1 有最近的便宜站点
				if(curCap * AVG >= v[cheapestPos].position - curPos){// 当前的油量可以直接到达
					curCap -= (v[cheapestPos].position - curPos)/AVG;
				}else{// 需要加油才能到达
					sumPrice += ((v[cheapestPos].position - curPos) / AVG - curCap) * curPrice; 
					curCap = 0;
				}
				curIndex = cheapestPos;
			}else{ // 2. 范围内的站点都比当前点贵,先看是否可以直接到达终点,否则把油加满开到最便宜的点。
				if(curPos + fullDis >= D){// 可以直接到达终点
					if(curCap * AVG <= D - curPos){// 2.1.1 油不足够跑到终点,先加油
						sumPrice += ((D - curPos) / AVG - curCap) * curPrice;
						curCap = 0;
					}else{
						curCap -= (D - curPos) / AVG;
					}
					break;
				}
				for(int i = curIndex+1; i < v.size(); i++){
					if(v[i].position > curPos + fullDis)break;
					if(v[i].price < v[cheapestPos2].price){
						cheapestPos2 = i;
					}
				}
				sumPrice += (C - curCap) * curPrice;
				curCap = C - (v[cheapestPos2].position - curPos) / AVG;
				curIndex = cheapestPos2;
			}
		}else{// 2. 范围内无可达站点
			if(curPos + fullDis >= D){// 2.1 可以直接到目的地
				if(curCap*AVG <= D - curPos){// 2.1.1 油不足够跑到终点,先加油
					sumPrice += ((D - curPos) / AVG - curCap) * curPrice;
					curCap = 0;
				}else{
					curCap -= (D - curPos)/AVG;
				}
				break;
			}else{
				cout << "The maximum travel distance = "<<fixed << setprecision(2) << curPos + fullDis << endl;
				return 0;
			}
		}
	}
	// cout << "curCap  = " << curCap << endl;
	cout <<fixed << setprecision(2) << sumPrice << endl;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值