To Fill or Not to Fill 九度教程第25题 较为复杂的贪心策略

题目链接

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.
输入描述:
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
输入
50 1300 12 8
6.00 1250
7.00 600
7.00 150
7.10 0
7.20 200
7.50 400
7.30 1000
6.85 300
50 1300 12 2
7.10 0
7.00 600
输出
749.17
The maximum travel distance = 1200.00

题目大意:汽车从杭州出发前往目的地,但是油箱的容量是有限的,路上有很多加油站,每个加油站的价格不同。为汽车设计一个从杭州到终点的最便宜的加油策略,Cmax表示油箱最大容量,D表示杭州到目的地的距离,Davg表示平均每单位的汽油可以让汽车行驶的距离,N表示汽车的站点数量,每个站点都会给出它的单位油价Pi和汽车站点和杭州的距离Di,求汽车从杭州到终点的最小花费,如果不能够到达,就输出汽车能够行驶的最大距离。

解题思路:
贪心策略
准备工作:

1. 为了方便考虑边界,将终点也视为一个加油站且价格无限低,因此我们总是空箱到达终点,这样的话就不会有油量剩余;
2. (这一步不做也可以,只是博主的习惯^_^)将题目中给出的加油站到起点的距离转化为各个加油站到终点的距离,然后按照距离从大到小排序。

因为将终点也视为了一个加油站,则共有n+1个站点
对于当前站点tmp,所能到达的最大范围即满油量所能到达的距离,设满油量能前进的距离为max_len,则在tmp.pos到tmp.pos-max_len范围内,分如下情况进行考虑:

1、此范围内有加油站
①若能够到达一家更便宜的加油站,则立即空箱到达。这样的话就能使用更加便宜的油了,这里找到的便宜加油站是离得最近的便宜加油站,而不一定是最便宜的加油站。
②如果能够到达的汽油站都更贵,则满箱出发,在能够到达的最便宜的汽油站加油。这样的话我们就能尽量使用便宜的价格了。

2、此范围内无加油站
因为终点也算加油站,这种情况下肯定是到达不了终点,只需要把油加满,能跑多远跑多远。

这里来看一下对样例1的模拟过程:

7.1   7.0   7.2   6.85  7.5  7.0  7.3   6.0  0.0
1300  1150  1100  1000  900  700  300   50   0
满箱能够前进的最大距离为600
在起始位置1处,能够到达更加便宜的站点2(价格为7.0),则适量补充油量后,空箱到达
在站点2出,能够到达更加便宜的站点4(价格为6.85),则适量补充油量后,空箱到达
在站点4处,能够到达的站点价格都更贵,则加满油后,到达最便宜的站点6
在站点6处,能够到达的站点价格都更贵,则加满油后,到达最便宜的站点7
在站点7处,能够到达更加便宜的站点8(价格为6.0),则适量补充油量后,空箱到达
在站点8处,能够到达更加便宜的站点9(价格为0.0),则适量补充油量后,空箱到达

AC代码:

/*贪心策略:
站在一家加油站上开始考虑
若能够到达一家更便宜的加油站 则立即空箱到达;
如果能够到达的汽油站都更贵,则满箱出发,在能够到达的最便宜的汽油站加油
*/
#include<iostream>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define maxn 510
#define INF 0x3f3f3f3f
struct node {
	double price;
	int pos;
}list[maxn];
bool cmp(node a, node b) {
	return a.pos > b.pos;
}
int main() {
	double c;
	int l, num, n;
	while (cin >> c >> l >> num >> n) {
		int max_len = c*num;
		for (int i = 1; i <= n; i++) {
			cin >> list[i].price >> list[i].pos;
			list[i].pos = l - list[i].pos;
		}
		list[n + 1].price = 0; list[n + 1].pos = 0;//将终点也当作一个加油站 价格最低 因此是空箱到达
		sort(list + 1, list + 1 + n+1, cmp);
		//特殊判断起始位置有没有加油站
		//测试用例中貌似没有这样的情况,没有这段单独判断的代码也可以
		if (n == 0 || list[1].pos != l) {
			printf("The maximum travel distance = 0.00\n");
			continue;
		}
		int tmp = 1; double ans = 0.0;//tmp是目前所处的加油站 ans是总的费用
		double tmp_c = 0;//到达加油站时的余量
		while (tmp <= n) {//在加油站上开始考虑
			int now = tmp + 1;//考虑后面的加油站
			double min_price = INF;//目前最低的价格
			int min_n = now;//最低价格的汽油站编号
			bool flag = false;//用于判断是否是空箱到达
			if (list[tmp].pos - list[now].pos > max_len) {//如果不能到达之后的加油站
				ans = l - list[tmp].pos + max_len;//这里ans用做距离
				printf("The maximum travel distance = ");
				break;
			}
			while (now<=n+1&&list[tmp].pos - list[now].pos <= max_len) {//注意now有范围限制
				if (list[now].price < list[tmp].price) {//空箱达到的情况
					int juli = list[tmp].pos - list[now].pos;//计算距离
					double cost = (juli*1.0 / num - tmp_c) * list[tmp].price;//计算代价
					ans += cost;
					tmp = now;
					tmp_c = 0;//空箱到达
					flag = true;
					break;
				}
				else {//满箱出发
					if (list[now].price < min_price) {//记录最低价格以及相应的位置
						min_price = list[now].price;
						min_n = now;
					}
					now++;
				}
			}
			if (flag)continue;//空箱到达
			ans += (c-tmp_c)*list[tmp].price;//满箱出发 即在当前的加油站将邮箱加满
			int juli = list[tmp].pos - list[min_n].pos;
			tmp_c = c - juli*1.0 / num;//剩余油量
			tmp = min_n;//当前位置
		}
		printf("%.2f\n", ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章