Leetcode 871.最低加油次数(dp / 贪心+优先队列)
作者:互联网
考虑可以用多种解法解决该题。
首先很容易想到用$O(n^2)$的递推dp。设$d[i][j]$为到达第i站前加油次数为j时的最大油量,最后直接找终点最小值就行。鉴于数据规模比较小,stations.length<=500,因此$O(n^2)$是可以通过的。
long long d[503][503]; class Solution { public: int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) { memset(d,-1,sizeof(d)); int lim=stations.size(); vector<int> temp;temp.push_back(target),temp.push_back(0); stations.push_back(temp); d[0][0]=startFuel-stations[0][0]; if (d[0][0]<0) //第0站都开不到 return -1; for (int i=1;i<=lim;i++){ d[i][0]=(long long)((d[i-1][0]-(stations[i][0]-stations[i-1][0]))>=0?d[i-1][0]-(stations[i][0]-stations[i-1][0]):-1); long long a,b; for (int j=1;j<=lim;j++){ if (d[i-1][j]>=0) a=(long long)(d[i-1][j]-(stations[i][0]-stations[i-1][0])); //上一站不加 else a=-1; if (d[i-1][j-1]>=0) b=(long long)(d[i-1][j-1]+stations[i-1][1]-(stations[i][0]-stations[i-1][0])); //上一站加 else b=-1; if (a>=0||b>=0) d[i][j]=a>b?a:b; } } for (int i=0;i<=lim;i++) if (d[lim][i]>=0) return i; return -1; } }; //d[i][j]到达第i个站之前加油次数为j次时剩余油量 //d[i+1][j]=max{d[i][j],d[i][j-1]+stations[i][1]}-(stations[i+1][0]-stations[i][0])
打完后会发现这种解法的效率是比较低下的,考虑其他更优的解法。很容易想到或许可以用贪心:在某个点带着油出发后,我们先考虑一口气走到没油为止。如果能走到终点当然最好;如果走不到终点,在油耗尽的位置(设为位置A),我们再回头挑选来路的加油站,再看本应该在哪个加油站加。
挑选来路的加油站时,假设有B、C、D三个加油站,且分别有b、c、d升的油,如下图:
显然这时应当选择的最优加油站是油最多的那个。因为路已经走过了已经消耗过油了,我们从来路的加油站提取油是不用消耗路程的。因此只需要用一个堆(或者优先队列)维护来路上最多油的站。
复杂度是$O(nlogn)$。
class Solution { public: int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) { int cnt=0; priority_queue<int> que; int line=startFuel,lim=stations.size(); for (int i=0;i<lim;i++) if (line>=stations[i][0]) //可以到 que.push(stations[i][1]); else{ //不能到,需要加油 while (line<stations[i][0]){ if (que.empty()) //无油可加 return -1; line+=que.top(),que.pop(),cnt++; } que.push(stations[i][1]); } while (line<target){ if (que.empty()) //无油可加 return -1; line+=que.top(),que.pop(),cnt++; } return cnt; } };
标签:temp,stations,int,long,871,加油站,startFuel,Leetcode,dp 来源: https://www.cnblogs.com/wegret/p/16438280.html