算法基礎知識——貪心策略

算法基礎知識——貪心策略

目錄:

  1. 基礎知識
    1. 貪心策略(自頂向下設計、局部最優、無後效性、最優子結構)
    2. 活動選擇問題
    3. 動態規劃算法和貪心算法區別
  2. 應用實例
    1. 雞兔同籠【北京大學】
    2. FatMouse' Trade【王道機試】
    3. Senior's Gun【王道機試】
    4. 代理服務器【清華大學】
    5. 今年暑假不AC【王道機試】
    6. Case of Fugitive【Codeforces】
    7. To Fill or Not to Fill【浙江大學】

一、基礎知識

1、貪心策略:

  • 定義:總是做出局部最優的選擇。對於特定的最優化問題,貪心策略保證一定能夠收斂到全局最優解。
  • 貪心算法自頂向下的設計:做出一個選擇,然後求解剩下的那個子問題,而不是自底向上地求解出很多子問題,然後再做出選擇。
  • 術語定義:
    • 無後效性:某個狀態以前的過程不會影響以後的狀態,而只與當前狀態有關。
    • 最優子結構:如果一個問題的最優解包含其子問題的最優解,則稱此問題具有最優子結構性質。
  • 核心思想:總是選擇當前狀態下最優的策略。並不以整體最優進行考慮,而只考慮當前這一步,可獲得局部最優解。
  • 應用場景:
    • 在遇到求最大、最小、最多等多值問題時,應優先考慮用貪心策略求解。
    • 若問題滿足最優子結構性質,即該問題無後效性,那麼全局的最優解便可由求子問題的最優解得到。
  • 常見題型:
    • 簡單貪心
    • 區間貪心:當有多個不同的區間存在,且這些區間有可能相互重疊的時候,如何選擇才能從衆多區間中,選取最多的兩兩互不相交的區間。
  • 貪心算法設計過程:
    • 確定問題的最優子結構
    • 設計一個遞歸算法
    • 證明如果我們做出一個貪心選擇,只剩下一個子問題
    • 證明做出貪心選擇後,原問題總是存在最優解,即貪心選擇總是安全的
    • 設計一個遞歸算法實現貪心策略
    • 將遞歸算法轉換爲迭代算法

2、活動選擇問題:

  • 問題描述:一個調度競爭共享資源的多個活動的問題,目標是選出一個最大的互相兼容的活動集合。
    • 兼容定義:每個活動Ai都有一個開始時間Si和結束時間Fi,如果被選中,任務Ai發生在[ Si, Fi )期間。如果兩個活動Ai和Aj滿足[ Si, Fi )和[ Sj, Fj )不重疊,則稱它們是兼容的。
  • 在動態規劃策略中,有:
    • c[i, j] = 0,若Sij = ∅
    • c[i, j] = max{ c[i, k] + c[k, j] + 1},若Sij ≠ ∅
  • 在貪心策略中:
    • 思路:
      • 首先應該選擇這樣一個活動,選出它後剩下的資源應能被儘量多的其他任務所用。因此可以選擇最早結束的活動,記爲A1。
      • 然後選擇在A1結束後開始的活動。

3、動態規劃算法和貪心算法區別:

  • 動態規劃算法先求解子問題才能進行第一次選擇;貪心算法在進行第一次選擇之前不求解任何子問題。
  • 動態規劃算法是自底向上進行計算的(如果自頂向下求解,需要建立備忘機制);貪心算法通常是自頂向下的,進行一次又一次選擇,將給定問題實例變得更小。
  • 動態規劃算法的選擇通常依賴於子問題的解,貪心算法的選擇可能依賴於之前做出的選擇,但不依賴於任何將來的選擇或者子問題的解。

二、應用實例

1、題目描述:一個籠子裏面關了雞和兔子(雞有2只腳,兔子有4只腳,沒有例外)。已經知道了籠子裏面腳的總數a,問籠子裏面至少有多少隻動物,至多有多少隻動物。【北京大學】

  • 輸入格式:每組測試數據佔1行,每行一個正整數a (a < 32768)
  • 輸出格式:輸出包含n行,每行對應一個輸入,包含兩個正整數,第一個是最少的動物數,第二個是最多的動物數,兩個正整數用一個空格分開。如果沒有滿足要求的答案,則輸出兩個0。
  • 樣例輸入:
    • 2
    • 3
    • 20
  • 樣例輸出:
    • 0 0
    • 5 10

示例代碼1:

#include <iostream>

using namespace std;

const int RABBIT_FOOT = 4;
const int CHICKEN_FOOT = 2;

int main(){
	int foot;
	while(cin >> foot){
		int tmp1 = foot, tmp2 = foot;
		int most = 0, less = 0;
		while(tmp1 >= CHICKEN_FOOT){
			most++;
			tmp1 -= CHICKEN_FOOT;
		}
		while(tmp2 >= RABBIT_FOOT){
			less++;
			tmp2 -= RABBIT_FOOT;
		}
		while(tmp2 >= CHICKEN_FOOT){
			less++;
			tmp2 -= CHICKEN_FOOT;
		}
		if(tmp1 == 0 && tmp2 == 0){
			cout << less << " " << most << endl;
		}else{
			cout << "0 0" << endl;
		}
	}
	return 0;
}

示例代碼2:

#include <iostream>

using namespace std;

int main(){
	int foot;
	while(cin >> foot){
		int most = 0, less = 0;
		if(foot % 2 == 0){
			most = foot / 2;
			less = foot / 4 + (foot % 4) / 2;
		}
		cout << less << " " << most << endl;
	}
	return 0;
}

2、題目描述:FatMouse prepared M pounds of cat food, ready to trade with the cats guarding the warehouse containing his favorite food, JavaBean. The warehouse has N rooms. The i-th room contains J[i] pounds of JavaBeans and requires F[i] pounds of cat food. FatMouse does not have to trade for all the JavaBeans in the room, instead, he may get J[i]* a% pounds of JavaBeans if he pays F[i]* a% pounds of cat food. Here a is a real number. Now he is assigning this homework to you: tell him the maximum amount of JavaBeans he can obtain. 【王道機試】

  • 輸入格式:The input consists of multiple test cases. Each test case begins with a line containing two non-negative integers M and N. Then N lines follow, each contains two non-negative integers J[i] and F[i] respectively. The last test case is followed by two -1's. All integers are not greater than 1000.
  • 輸出格式:For each test case, print in a single line a real number accurate up to 3 decimal places, which is the maximum amount of JavaBeans that FatMouse can obtain. 
  • 樣例輸入:
    • 5 3
    • 7 2
    • 4 3
    • 5 2
    • 20 3
    • 25 18
    • 24 15
    • 15 10
    • -1 -1
  • 樣例輸出:
    • 13.333
    • 31.500

示例代碼:

#include <iostream>
#include <queue>
#include <iomanip>

using namespace std;

struct Mouse{
	double javabean;
	double catF;
	Mouse(double j, double c):javabean(j), catF(c){};
};

bool operator<(const Mouse &m1, const Mouse &m2){
	if(m1.catF == 0 && m2.catF == 0){
		return m1.javabean < m2.javabean;
	}
	if(m1.catF == 0){
		return false;
	}
	if(m2.catF == 0){
		return true;
	}
	return m1.javabean / m1.catF < m2.javabean / m2.catF;
}

priority_queue<Mouse> myQueue;

int main(){
	double M, N;
	while(cin >> M >> N && M != -1 && N != -1){
		double f, j;
		for(int i = 0; i < N; i++){
			cin >> j >> f;
			Mouse mouse(j, f);
			myQueue.push(mouse);
		}
		double answer = 0;
		while(M >= 0 && myQueue.size() > 0){
			if(M - myQueue.top().catF >= 0){
				M -= myQueue.top().catF;
				answer += myQueue.top().javabean;
				myQueue.pop();
			}else{
				answer += M * (myQueue.top().javabean / myQueue.top().catF);
				break;
			}
		}
		cout << fixed << setprecision(3) << answer << endl;
		if(!myQueue.empty()){
			myQueue.pop();
		}
	}
	return 0;
}

3、題目描述:Xuejiejie is a beautiful and charming sharpshooter.She often carries n guns, and every gun has an attack power a[i].One day, Xuejiejie goes outside and comes across m monsters, and every monster has a defensive power b[j].Xuejiejie can use the gun i to kill the monster j, which satisfies b[j] ≤ a[i], and then she will get a[i]-b[j] bonus.Remember that every gun can be used to kill at most one monster, and obviously every monster can be killed at most once.Xuejiejie wants to gain most of the bonus. It's no need for her to kill all monsters.【王道機試】

  • 輸入格式:In the first line there is an integer T, indicates the number of test cases.In each case:The first line contains two integers n,m.The second line contains n integers, which means every gun's attack power.The third line contains m integers, which mean every monster's defensive power.1 ≤ n,m ≤ 100000, -10^9 ≤ a[i],b[j] ≤ 10^9.
  • 輸出格式:For each test case, output one integer which means the maximum of the bonus Xuejiejie could gain.
  • 樣例輸入:
    • 1
    • 2 2
    • 2 3
    • 2 2
  • 樣例輸出:
    • 1

示例代碼:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

vector<int> monster;
vector<int> gun;

int main(){
	int number;
	while(cin >> number){
		int n, m, monsterPower, gunPower;
		for(int i = 0; i < number; i++){
			cin >> n >> m;
			for(int j = 0; j < n; j++){
				cin >> gunPower;
				gun.push_back(gunPower);
			}
			for(int j = 0; j < m; j++){
				cin >> monsterPower;
				monster.push_back(monsterPower);
			}
			sort(gun.begin(), gun.end());
			sort(monster.begin(), monster.end());
			int bonus = 0, loc = 0;
			bool flag = false;
			//強槍殺弱怪
			for(int j = gun.size() - 1; j >= 0; j--){
				if(loc == monster.size() || flag){  //殺到最後一隻怪了
					break;
				}
				if(monster[loc] <= gun[j]){
					bonus += gun[j] - monster[loc];
					loc++;
				}else{  //你打不過任何一隻怪了
					flag = true;
				}
			}
			cout << bonus << endl;
			monster.clear();
			gun.clear();
		}
	}
	return 0;
}

4、題目描述:使用代理服務器能夠在一定程度上隱藏客戶端信息,從而保護用戶在互聯網上的隱私。我們知道n個代理服務器的IP地址,現在要用它們去訪問m個服務器。這 m 個服務器的 IP 地址和訪問順序也已經給出。系統在同一時刻只能使用一個代理服務器,並要求不能用代理服務器去訪問和它 IP地址相同的服務器(不然客戶端信息很有可能就會被泄露)。在這樣的條件下,找到一種使用代理服務器的方案,使得代理服務器切換的次數儘可能得少。【清華大學】

  • 輸入格式:每個測試數據包括 n + m + 2 行。第 1 行只包含一個整數 n,表示代理服務器的個數。第 2行至第n + 1行每行是一個字符串,表示代理服務器的 IP地址。這n個 IP地址兩兩不相同。第 n + 2 行只包含一個整數 m,表示要訪問的服務器的個數。第 n + 3 行至第 n + m + 2 行每行是一個字符串,表示要訪問的服務器的 IP 地址,按照訪問的順序給出。每個字符串都是合法的IP地址,形式爲“xxx.yyy.zzz.www”,其中任何一部分均是0–255之間的整數。輸入數據的任何一行都不包含空格字符。其中,1<=n<=1000,1<=m<=5000。
  • 輸出格式:可能有多組測試數據,對於每組輸入數據, 輸出數據只有一行,包含一個整數s,表示按照要求訪問服務器的過程中切換代理服務器的最少次數。第一次使用的代理服務器不計入切換次數中。若沒有符合要求的安排方式,則輸出-1。
  • 樣例輸入:
    • 3
    • 166.111.4.100
    • 162.105.131.113
    • 202.112.128.69
    • 6
    • 72.14.235.104
    • 166.111.4.100
    • 207.46.19.190
    • 202.112.128.69
    • 162.105.131.113
    • 118.214.226.52
  • 樣例輸出:
    • 1

示例代碼:

#include <iostream>
#include <string>
#include <vector>

using namespace std;

vector<string> serverAddress;
vector<string> agentAddress;

int main(){
	int n, serverCount;
	while(cin >> n){
		string ip;
		for(int i = 0; i < n; i++){
			cin >> ip;
			agentAddress.push_back(ip);
		}
		cin >> serverCount;
		for(int i = 0; i < serverCount; i++){
			cin >> ip;
			serverAddress.push_back(ip);
		}
		int answer = 0;
		bool flag = false;
		for(int i = 0; i < serverAddress.size(); ){
			int maxCount = 0;
			for(int j = 0; j < agentAddress.size(); j++){
				int count = 0, tmp = i;
				while(tmp < serverAddress.size() && serverAddress[tmp] != agentAddress[j]){
					count++;
					tmp++;
				}
				if(count > maxCount){
					maxCount = count;
				}
			}
			if(maxCount == 0){
				flag = true;
				break;
			}
			i += maxCount;
			answer++;
		}
		if(flag){
			cout << -1 << endl;
		}else{
			cout << answer - 1 << endl;
		}
		serverAddress.clear();
		agentAddress.clear();
	}
	return 0;
}

5、題目描述:“今年暑假不AC?”
“是的。”
“那你幹什麼呢?”
“看世界盃呀,笨蛋!”
確實如此,世界盃來了,球迷的節日也來了,估計很多ACMer也會拋開電腦,奔向電視了。
作爲球迷,一定想看盡量多的完整的比賽,當然,作爲新時代的好青年,你一定還會看一些其它的節目,如《新聞聯播》(永遠不要忘記關心國家大事)、《非常6+7》、《超級女生》,以及王小丫的《開心辭典》等等,假設你已經知道了所有你喜歡看的電視節目的轉播時間表,你會合理安排嗎?(目標是能看盡量多的完整節目)【王道機試】

  • 輸入格式:輸入數據包含多個測試實例,每個測試實例的第一行只有一個整數n(n<=100),表示你喜歡看的節目的總數,然後是n行數據,每行包括兩個數據Ti_s,Ti_e (1<=i<=n),分別表示第i個節目的開始和結束時間,爲了簡化問題,每個時間都用一個正整數表示。n=0表示輸入結束,不做處理。
  • 輸出格式:對於每個測試實例,輸出能完整看到的電視節目的個數,每個測試實例的輸出佔一行。
  • 樣例輸入:
    • 12
    • 1 3
    • 3 4
    • 0 7
    • 3 8
    • 15 19
    • 15 20
    • 10 15
    • 8 18
    • 6 12
    • 5 10
    • 4 14
    • 2 9
    • 0
  • 樣例輸出:
    • 5

示例代碼:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct TV{
	int startTime;
	int endTime;
	TV(int s, int e):startTime(s), endTime(e){};
};

vector<TV> tvList;

bool CompareAsc(const TV &t1, const TV &t2){
	if(t1.endTime == t2.endTime){
		return t1.startTime > t2.startTime;
	}else{
		return t1.endTime < t2.endTime;
	}
}

int main(){
	int n;
	while(cin >> n && n != 0){
		int st, et;
		for(int i = 0; i < n; i++){
			cin >> st >> et;
			TV tv(st, et);
			tvList.push_back(tv);
		}
		sort(tvList.begin(), tvList.end(), CompareAsc);
		int answer = 0, currentTime = 0;
		for(int i = 0; i < tvList.size(); i++){
			if(currentTime <= tvList[i].startTime){
				answer++;
				currentTime = tvList[i].endTime;
			}
		}
		cout << answer << endl;
		tvList.clear();
	}
	return 0;
}

6、題目描述:Andrewid the Android is a galaxy-famous detective. He is now chasing a criminal hiding on the planet Oxa-5, the planet almost fully covered with water.
The only dry land there is an archipelago of n narrow islands located in a row. For more comfort let's represent them as non-intersecting segments on a straight line: island i has coordinates [l i , r i ], besides, r i  < l i + 1 for 1 ≤ i ≤ n - 1.
To reach the goal, Andrewid needs to place a bridge between each pair of adjacent islands. A bridge of length a can be placed between the i -th and the (i + 1)-th islads, if there are such coordinates of x and y , that l i  ≤ x ≤ r i , l i + 1 ≤ y ≤ r i + 1 and y - x = a .
The detective was supplied with m bridges, each bridge can be used at most once. Help him determine whether the bridges he got are enough to connect each pair of adjacent islands.【Codeforces】

  • 輸入格式:The first line contains integers n (2 ≤ n ≤ 2*10^5) and m (1 ≤ m ≤ 2*10^5) — the number of islands and bridges.
    Next n lines each contain two integers li and ri (1 ≤ li ≤ ri ≤ 10^18) — the coordinates of the island endpoints.
    The last line contains minteger numbers a1, a2, ..., am (1 ≤ ai ≤ 10^18) — the lengths of the bridges that Andrewid got.
  • 輸出格式:If it is impossible to place a bridge between each pair of adjacent islands in the required manner, print on a single line "No" (without the quotes), otherwise print in the first line "Yes" (without the quotes), and in the second line print n - 1 numbers b1, b2, ..., bn - 1, which mean that between islands i and i + 1 there must be used a bridge number bi. 
    If there are multiple correct answers, print any of them. Note that in this problem it is necessary to print "Yes" and "No" in correct case.
  • 備註:In the first sample test you can, for example, place the second bridge between points 3 and 8, place the third bridge between points 7 and 10 and place the first bridge between points 10 and 14.In the second sample test the first bridge is too short and the second bridge is too long, so the solution doesn't exist.
  • 樣例輸入:
    • 4 4
    • 1 4
    • 7 8
    • 9 10
    • 12 14
    • 4 5 3 8
    • 2 2
    • 11 14
    • 17 18
    • 2 9
    • 2 1
    • 1 1
    • 1000000000000000000 1000000000000000000
    • 999999999999999999
  • 樣例輸出:
    • Yes
    • 2 3 1 
    • No
    • Yes

示例代碼:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

struct Island{
	long long bLeft;
	long long bRight;
	int index;
	Island(long long bl, long long br, int i):bLeft(bl), bRight(br),index(i){};
};

struct TwoIslandDistance{
	long long minLength;
	long long maxLength;
	int index;
	TwoIslandDistance(long long min, long long max, int i):minLength(min), maxLength(max), index(i){};
};

struct Bridge{
	int index;
	long long bridgeLength;
	Bridge(int i, long long b):index(i), bridgeLength(b){};
};

struct Answer{
	int bridgeIndex;
	int islandIndex;
	Answer(int b, int i):bridgeIndex(b), islandIndex(i){};
};

vector<Island> islandList;
vector<TwoIslandDistance> distanceList;
vector<Bridge> bridgeList;
vector<Answer> answerList;

bool IslCompareAsc(const Island &i1, const Island &i2){
	return i1.bLeft < i2.bLeft;
}

bool DisCompareAsc(const TwoIslandDistance &t1, const TwoIslandDistance &t2){
	if(t1.minLength == t2.minLength){
		return t1.maxLength < t2.maxLength;
	}else{
		return t1.minLength < t2.minLength;
	}
}

bool BriCompareAsc(const Bridge &b1, const Bridge &b2){
	return b1.bridgeLength < b2.bridgeLength;
}

bool AnswerCompareAsc(const Answer &a1, const Answer &a2){
	return a1.islandIndex < a2.islandIndex;
}

int main(){
	int bridgeNumber, islandNumber;
	long long bLength, iLeft ,iRight;
	while(cin >> islandNumber >> bridgeNumber){
		for(int i = 0; i < islandNumber; i++){
			cin >> iLeft >> iRight;
			Island island(iLeft, iRight, i);
			islandList.push_back(island);
		}
		sort(islandList.begin(), islandList.end(), IslCompareAsc);
		long long min, max;
		for(int i = 0; i < islandList.size() - 1; i++){
			min = islandList[i + 1].bLeft - islandList[i].bRight;
			max = islandList[i + 1].bRight - islandList[i].bLeft;
			TwoIslandDistance distance(min, max, i);
			distanceList.push_back(distance);
		}
		sort(distanceList.begin(), distanceList.end(), DisCompareAsc);
		for(int i = 1; i <= bridgeNumber; i++){
			cin >> bLength;
			Bridge bridge(i, bLength);
			bridgeList.push_back(bridge);
		}
		sort(bridgeList.begin(), bridgeList.end(), BriCompareAsc);
		bool flag = false;
		int loc = 0;
		for(int i = 0; i < bridgeList.size(); i++){
			if(bridgeList[i].bridgeLength <= distanceList[loc].maxLength 
				&& bridgeList[i].bridgeLength >= distanceList[loc].minLength){
					Answer answer(bridgeList[i].index, distanceList[loc].index);
					answerList.push_back(answer);
					loc++;
			}
			if(loc == distanceList.size()){
				flag = true;
				break;
			}
		}
		if(flag){
			cout << "Yes" << endl;
			sort(answerList.begin(), answerList.end(), AnswerCompareAsc);
			for(int i = 0; i < answerList.size(); i++){
				cout << answerList[i].bridgeIndex << " ";
			}
			cout << endl;
		}else{
			cout << "No" << endl;
		}
		bridgeList.clear();
		distanceList.clear();
		answerList.clear();
		islandList.clear();
	}
	return 0;
}

7、題目描述: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.
  • 樣例輸入:
    • 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

示例代碼:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iomanip>

using namespace std;

struct Station{
	double gasPrice;
	double statDist;
	Station(double g, double d):gasPrice(g), statDist(d){};
};

bool CompareAsc(const Station &s1, const Station &s2){
	if(s1.statDist == s2.statDist){
		return s1.gasPrice < s2.gasPrice;
	}else{
		return s1.statDist < s2.statDist;
	}
}

int main(){
	vector<Station> statList;
	double tankSize, distance, distPerOil, station;
	while(cin >> tankSize >> distance >> distPerOil >> station){
		double gasPrice, distStation;
		for(int i = 0; i < station; i++){
			cin >> gasPrice >> distStation;
			Station s(gasPrice, distStation);
			statList.push_back(s);
		}
		sort(statList.begin(), statList.end(), CompareAsc);
		double maxDist = tankSize * distPerOil;
		double price = 0.0, currDist = 0.0, currTankSize = 0.0;
		int currStat = 0;
		double maxArrive = 0.0;
		bool flag = true; //能到達目的地爲True
		if(statList[0].statDist != 0){
			flag = false;
		}
		while(currDist < distance){
			if(currStat < statList.size() - 1){
				double dist = statList[currStat + 1].statDist - statList[currStat].statDist;
				if(dist > maxDist){
					maxArrive = currDist + maxDist;
					flag = false;
					break;
				}
			}
			//如果下一個加油站油比本加油站便宜,加上能到下一個加油站的油就好
			if(currStat < statList.size() - 1
				&& statList[currStat + 1].gasPrice <= statList[currStat].gasPrice){
				double dist = statList[currStat + 1].statDist - statList[currStat].statDist;
				double remainOil = dist / distPerOil - currTankSize;
				if(remainOil > 0){ //當前油量不足
					price += remainOil * statList[currStat].gasPrice;
					currTankSize = 0.0;
				}else{ //當前油量充足,能去下一個加油站,不加油
					currTankSize -= dist / distPerOil;
				}
				currDist += dist;
				currStat++;
			}else{
				if(currStat == statList.size() - 1){ //最後一個油站
					double remainDist = distance - currDist;
					if(remainDist > maxDist){
						maxArrive = currDist + maxDist;
						flag = false;
						break;
					}
					double remainOil = remainDist / distPerOil - currTankSize;
					if(remainOil > 0){ //當前油箱中的油不足以去目的地
						price += remainOil * statList[currStat].gasPrice;
					}
					currDist = distance;
				}
				//找到滿箱油路程中比本油站價格低的油站,加上能到這油站的油
				//如果沒找到,判斷是否能到目的地,如果能,加到達目的地的油,如果不能,加滿箱油
				else{
					double available = statList[currStat].statDist + maxDist;
					int next = currStat + 1, min_stat = currStat;
					double minPrice = statList[currStat].gasPrice;
					bool flag1 = true;//沒找到價格更低的油站爲True
					while(next != statList.size() 
						&& statList[next].statDist <= available){ //找到下一個最便宜的油站
						if(statList[next].gasPrice < minPrice){
							minPrice = statList[next].gasPrice;
							min_stat = next;
							flag1 = false;
							break;
						}
						next++;
					}
					if(flag1){ //沒找到價格更低的油站
						if(distance - currDist > maxDist){ //加滿的情況
							price += (tankSize - currTankSize) * statList[currStat].gasPrice;
							Station next = statList[currStat + 1];
							currDist = next.statDist;
							currTankSize = tankSize - (currDist - statList[currStat].statDist) / distPerOil;
						}else{
							double oilSize = currTankSize - (distance - currDist) / distPerOil;
							if(oilSize < 0){ 
								price += oilSize * (-1) * statList[currStat].gasPrice;
							}
							currDist = distance;
						}
						currStat++;
					}else{
						double dist = statList[min_stat].statDist - statList[currStat].statDist;
						double remainOil = dist / distPerOil - currTankSize;
						//當前油箱中的油不足以去該油站
						if(remainOil > 0){
							price += remainOil * statList[currStat].gasPrice;
							currTankSize = 0.0;
						}else{
							currTankSize -= dist / distPerOil;
						}
						currDist += dist;
						currStat = min_stat;
					}
				}
			}
		}
		if(flag){
			cout << fixed << setprecision(2) << price << endl;
		}else{
			cout << "The maximum travel distance = " << fixed << setprecision(2) << maxArrive << endl;
		}
		statList.clear();
	}
	return 0;
}

附註:

(1)給出的測試樣例中,

50 1300 12 8
7.10 0           7.10 * (150 / 12) = 88.75
7.00 150       7.00 * (300 - 150) /12 = 87.5
7.20 200
6.85 300       6.85 * 50 = 342.5
7.50 400
7.00 600       用掉了(600 - 300) / 12 = 25,剩餘50 - 25 = 25,加滿:7.00 * 25 = 175
7.30 1000     加多50公里路程的油, 7.30 * 50 / 12 = 30.42
6.00 1250     加多50公里路程的油, 6.00 * 50 / 12 = 25

88.75 + 87.5 + 342.5 + 175 + 30.42 + 25 = 749.17

參考文獻:

[1]Thomas.H.Cormen Charles E. Leiseron、Ronald L. Rivest Clifford Srein. 算法導論(第3版). [M]北京:機械工業出版社,2013.01;
[2]楊澤邦、趙霖. 計算機考研——機試指南(第2版). [M]北京:電子工業出版社,2019.11;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章