POJ 2431 Expedition 贪心
主要算法思路:
假设他们的车可以瞬间移动到之前到达过段任意加油站加油。显然,将油用光顺移回到加油站加油再顺移回来和直接经过加油站的时候加油,并且走到没有由从结果上来说时完全等价。
我们的策略是每次走到用光油,在瞬移回一下之前经过段加油站中油最多的地方加油,继续往前走,用光继续瞬移找经过的剩下的油最多段地方加油……直到到达城市,或将可加油全部加满也到达不了下一个加油站为止(这时返回-1)。
贪心性质证明:
设S{1,2,3…n}是所有加油站的集合,r为油箱中剩余油量,d为总路程。A{a1,a2,a3…am}是最优解。T{t1,t2…}是我们用贪心法得到的解,并按贪心的取解顺序排列(即)现证贪心解也是最优解。
若r≥d:则完全不需要加油,贪心法与最优解一样为空集。
若r < d:对i=1:若车在前r个单位的路上未加油,则必会停在离起点r处,无法到达。所以车必在前r的某个加油站加油。取A中在前r单位油量最大的加油站,不妨设为a1。又因为t1为前r路油量最大的加油站,所以有a1≤t1。易(A-{a1})U{t1}即A1{t1,a2,a3…am}也是最优解。
现假设对i=k(k<m),存在最优解Ak{t1,t2…tk,ak+1,ak+2…am},则当i=k+1时:若车在前r+t1+t2+..tk个单位的路上只加了k次油,那车最多只能停在离起点r+t1+t2+..tk处,无法到达(k<m若能到达,则与A是最优解矛盾),所以车在前r+t1+t2+..tk必须至少在加一次油,设取集合{ak+1,ak+2…am}在前r+t1+t2+..tk个单位的路上油量最大的加油站,不妨设为ak+1,因为tk+1是前r+t1+t2+..tk路程上除了t1到tk外最大的加油站,所以ak+1≤tk+1。所以Ak+1{t1,t2..tk+1,ak+2…,am}也是最优解。
综上,由数学归纳法,Am{t1,t2…tm}也是最优解,Am即为贪心解T。
证毕。
调试报告与算法优化:
本来采用的是multimap储存,而不是vector,但后来multimap刚刚超了20ms,应该是每次插入的时候都要保持有序耗费了较多的时间,改成先用vector储存,再用sort()排序就不超时了。后来,在大神的启发下,发现如果使用堆排序应该可以更加优化,而且走每一段路程只要把新遇到的加油站推入堆中,然后取堆顶元素就可以了,算法复杂度降到了O(nlogn)。
#include <iostream> #include <vector> #include <algorithm> using namespace std; //策略:每次考虑从起点到不加油所能到达的最远处,如果可以达到城市则停止,否则选择这段路上加油量最多的加油站进行加油。 //算法复杂度:O(n^2) class Stop { public: int dis;//distance from town to the stop int fuel;//amount of fuel at stop Stop(int a, int b) : dis(a), fuel(b){} bool operator <(const Stop b) const { if (fuel == b.fuel) return dis < b.dis; else return fuel > b.fuel; } }; vector<Stop> v; int n;//fuel stop number int tank;//fuel in the tank int len;//distance from the town int stopNeed = 0; int td, tf;//temp int solve() { //to solve the problem. //return the least number of stops needed //or return -1 when impossible to reach vector<Stop>::iterator j; for (int i = 0; i < n; i++) { if (tank >= len)//can get town return i; int tmp = tank; for (j = v.begin(); j != v.end(); j++) if (j->dis >= len-tank) { tank += j->fuel; v.erase(j); break; } if (tmp == tank)//tank didn't change.It means we can't find another stop to add fuel return -1; } return -1; } int main(void) { cin >> n; for (int i = 0; i < n; i++) { cin >> td >> tf; v.push_back(Stop(td, tf)); } cin >> len >> tank; //sort by fuel amount, from most to least.Same fuel unit, more distance from town is prior. sort(v.begin(), v.end()); cout << solve() << endl; //system("pause"); return 0; }