LeetCode/最低加油次数
汽车从起点出发驶向目的地,该目的地位于出发位置东面 target 英里处。
沿途有加油站,每个 station[i] 代表一个加油站,它位于出发位置东面 station[i][0] 英里处,并且有 station[i][1] 升汽油。
假设汽车油箱的容量是无限的,其中最初有 startFuel 升燃料。它每行驶 1 英里就会用掉 1 升汽油。
当汽车到达加油站时,它可能停下来加油,将所有汽油从加油站转移到汽车中。
为了到达目的地,汽车所必要的最低加油次数是多少?如果无法到达目的地,则返回 -1
1. 贪心算法
类似青蛙跳跃,更新维护最远加油站,不足以到达下一加油站时选取已经过加油站的最大油量
class Solution {
public:
int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
priority_queue<int> q;
stations.push_back({target, 0});//终点当做加油站
int last = 0, ans = 0;
for (int i = 0; i < stations.size(); ++i) {
auto need = stations[i][0] - last;//需要的油量
while (!q.empty() && startFuel < need) {
startFuel += q.top(); q.pop(); //取已走过加油站最大油量
ans++;//计数增加
}
if (startFuel >= need) {//油量满足需求
startFuel -= need;//到达下一加油站
q.push(stations[i][1]);//把油量加入优先队列
}
else return -1;
last = stations[i][0];//更新上一位置
}
return ans;//遍历结束说明能到达最后加油站,即终点
}
};
2. 动态规划(哈希表)
记录到达i加油站时的状态,即对应加油次数和所剩油量
后面的状态只需从前一个状态转移记录,当存在相同加油次数的路径时,取较大剩余油量
最后记录所有能达到最终目标的次数,更新取最小值
这里存在很多剪枝的细节,以及把起点和终点当加油站的处理方式,方便统一操作,减少判断
class Solution {
public:
int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
stations.push_back({target,0});//把终点当做加油站,统一处理
int n = stations.size();
vector<map<int,long>> dp(n+1);//表示到达i车站时对应加油次数和所剩油量,这里利用哈希表的自动排序
dp[0][0] = startFuel;
int prepos = 0;//记录上一个加油站的位置
int mincount = INT_MAX;
for(int i=1;i<=n;i++){//遍历得到所有加油站状态
//if(dp[i-1].size()==0) return -1;//不能到达前一个加油站,返回-1
for (auto& it : dp[i - 1]) {//从上一个加油站所有状态开始判断,从小到大
if(it.first>=mincount) break;//剪枝
if (it.second >= target - prepos) { mincount = min(it.first, mincount);break; }//剪枝
//如果能到达下一个加油站
int remain = it.second - (stations[i - 1][0] - prepos);//剩余油量
if (remain >= 0) {
//相同次数时,保留剩余油量多的
if(remain>dp[i][it.first])
dp[i][it.first] = remain;//不加油
if((long)remain + stations[i - 1][1]> dp[i][it.first + 1])
dp[i][it.first + 1] = (long)remain + stations[i - 1][1];//加油
}
}
prepos = stations[i - 1][0];//记录上一次加油站位置
}
if(mincount!=INT_MAX) return mincount;
return -1;
}
};
3. 动态规划(一维)
动态维护加油i次时的最远距离,新增加油站时,对所有值从后往前进行更新
dp[i]表示加油i次能到达的最远距离
这里其实也是经过二维动态规划压缩后的结果
class Solution {
public:
int minRefuelStops(int target, int startFuel, vector<vector<int>>& stations) {
vector<unsigned int> dp(stations.size() + 1);//dp[i]表示加油i次能到达的最远距离
dp[0] = startFuel;
//从前往后遍历考察每个加油站
for(int i = 0; i < stations.size(); i++)
//新增加油站后,从后往前更新dp表,从后往前才不会对下一个造成影响
for(int j = i; j >= 0; j--)
if(dp[j] >= stations[i][0])//如果加油j次能到达i加油站
dp[j + 1] = max(dp[j + 1], dp[j] + stations[i][1]);//更新j+1次的最远距离
for(int i = 0; i < dp.size(); i++)
if(dp[i] >= target) return i;//返回一个最小加油次数
return -1;
}
};