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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章