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;
    }
};
posted @ 2022-07-03 02:50  失控D大白兔  阅读(71)  评论(0编辑  收藏  举报