【4.贪心算法——编程题】

目录

1.简单

2.中等 

2.1买卖股票的最佳时机Ⅱ

2.2跳跃游戏

2.3加油站

3.困难

3.1跳跃游戏Ⅱ

1.简单

2.中等 

2.1买卖股票的最佳时机Ⅱ

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
 

典型的贪心算法,先分别求局部最优解,然后将局部最优解汇总。采用双指针法,min在前,max在后,当min找到当前局部的最小值(比如示例中的 1 ),max从min后再找局部最大值(示例中的5),p[max] - p[min]即为一个局部最优解(注意指针越界的情况)。

int maxProfit(vector<int>& prices) {
    int min = 0, max = 1;
    int profit = 0;
    int len = prices.size();
    while(min < len && max < len){
        while(min < len - 1 && prices[min + 1] <= prices[min]) //寻找局部最小值
            min++;
        max = min + 1;
        while(max < len - 1 && prices[max + 1] >= prices[max]) //寻找局部最大值
            max++;

        if(min < len && max < len){  //局部最优解
            profit += prices[max] - prices[min];
            min = max + 1;
            max += 2;
        }
    }
    return profit;
}

2.2跳跃游戏

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game
(面试的时候我可能真的做不出来=_=)

思路:尽可能到达最远位置(贪心)。
如果能到达某个位置,那一定能到达它前面的所有位置。

方法:初始化最远位置为0,然后遍历数组,如果当前位置能到达,并且当前位置+跳数>最远位置,就更新最远位置。最后比较最远位置和数组长度。

复杂度:时间复杂度O(n),空间复杂度O(1)。

作者:mo-lan-4
链接:https://leetcode-cn.com/problems/jump-game/solution/pythonji-bai-97kan-bu-dong-ni-chui-wo-by-mo-lan-4/

bool canJump(vector<int>& nums) {
    int max_jump = 0;
    for(int i = 0; i < nums.size(); i++){
        if(max_jump >= i)
            max_jump = max(max_jump, i + nums[i]);
    }
    return max_jump >= nums.size() - 1;
}

回溯算法虽然可以做,但会超时

bool traceback(vector<int>& nums, int begin){
    if(begin == nums.size() - 1){
        return true;
    }
    int furthest = nums[begin] + begin;
    if(nums[begin] + begin > nums.size() - 1)
        furthest = nums.size() - 1;
    for(int i = begin + 1; i <= furthest; i++){
        if(traceback(nums, i)){
            return true;
        }
    }
    return false;
}

bool canJump(vector<int>& nums){
    return traceback(nums, 0);
}

2.3加油站

在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升。

你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。

如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1。

说明: 

如果题目有解,该答案即为唯一答案。
输入数组均为非空数组,且长度相同。
输入数组中的元素均为非负数。
示例 1:

输入: 
gas  = [1,2,3,4,5]
cost = [3,4,5,1,2]

输出: 3

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/gas-station
暴力法:时间复杂度O(n),把每一个位置当为开始,看能不能走完这一圈;每次比较剩余汽油+加油>=路程才继续

int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
    int stock = 0;
    int flag = 0;
    for (int i = 0; i < gas.size(); ++i) {
        if(cost[i] > gas[i])
            continue;
        int j;
        for(j = i; j < gas.size(); j++){
            stock = stock + gas[j] - cost[j];
            if(stock < 0){
                stock = 0;
                flag = 1;
                break;
            }
        }
        if(flag == 1){
            flag = 0;
            continue;
        }
        for(j = 0; j < i; j++){
            stock = stock + gas[j] - cost[j];
            if(stock < 0){
                stock = 0;
                break;
            }
        }
        if(j == i)
            return i;
    }
    return -1;
}

 看了0ms的范例代码,确实佩服,只需要依次遍历,就能把开始位置找出来。

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int currentTank = 0;
        int totalTank = 0;
        int start = 0;

        for (int i = 0; i < gas.size(); i++) {
            totalTank += gas[i] - cost[i];  //如果totalTank最终<0,那肯定是走不通
            currentTank += gas[i] - cost[i];

            if (currentTank < 0) {  //从上一个start开始其实走不通,就把start变为i + 1尝试
                start = i + 1;
                currentTank = 0;
            }
        }
        return totalTank < 0 ? -1 : start;
    }
};

 

3.困难

3.1跳跃游戏Ⅱ

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
说明:

假设你总是可以到达数组的最后一个位置。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game-ii
之前的跳跃游戏I是用的回溯法,尽最大可能去尝试,不会超时,但不适用跳跃游戏II。这道题默认可以到达最终位置,所以采用贪心算法,查找每次能到达的最远位置作为下次搜索的end,再把这次的end赋值给下一次的begin,直到搜索到结尾。

int jump(vector<int>& nums){
    int begin = 0, end = 1, count = 0;
    while (end < nums.size()){
        int temp = 0;
        for (int i = begin; i < end; ++i) {
            temp = max(temp, i + nums[i]);
        }
        begin = end;
        end = temp + 1;
        count++;
    }
    return count;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值