面试初入算法(七)——贪心算法

贪心算法:指的是每一步都会选择最优解,但是合在一起不一样会产正最优解
贪心算法、回溯、动态规划的区别:

  1. 贪心:当下做局部最优判断,不能回退
  2. 回溯:可以回退,寻找解,不一定为最优解
  3. 动态规划:全局最优判断+回退

现在在题目中去体会什么是贪心算法:

1.在柠檬水摊上,每一杯柠檬水的售价为 5 美元。
顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

示例 1:

输入:[5,5,5,10,20]
输出:true
解释: 前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。 第 4位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。 第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。由于所有客户都得到了正确的找零,所以我们输出 true。

解法:
该题为leetcode中简单类型的题,因此就主要说下,这里那个思想用到了贪心
当收到20美元时,我们优先给钱方式为10+5元 而不是5 5 5元,这里优先使用大面值的钞票,即是我们所提到的贪心算法思想。

2.给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。

示例 1:

输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1

示例 2:

输入: coins = [2], amount = 3
输出: -1

解法:

贪心(来自于leetcode中大佬的思维)
11. 想要总硬币数最少,肯定是优先用大面值硬币,所以对 coins 按从大到小排序
12. 先丢大硬币,再丢会超过总额时,就可以递归下一层丢的是稍小面值的硬币

乘法对加法的加速
21. 优先丢大硬币进去尝试,也没必要一个一个丢,可以用乘法算一下最多能丢几个

    k = amount / coins[c_index] 计算最大能投几个
    amount - k * coins[c_index] 减去扔了 k 个硬币
    count + k 加 k 个硬币

    如果因为丢多了导致最后无法凑出总额,再回溯减少大硬币数量

最先找到的并不是最优解
31. 注意不是现实中发行的硬币,面值组合规划合理,会有奇葩情况
32. 考虑到有 [1,7,10] 这种用例,按照贪心思路 10 + 1 + 1 + 1 + 1 会比 7 + 7 更早找到
33. 所以还是需要把所有情况都递归完

ans 疯狂剪枝
41. 贪心虽然得不到最优解,但也不是没用的
42. 我们快速算出一个贪心的 ans 之后,虽然还会有奇葩情况,但是绝大部分普通情况就可以疯狂剪枝了
//参数解释,coins零钱容器,amount 总共的钱,index层数,count硬币数,ans最后返回结果
    void coinChange(vector<int>& coins, int amount, int index, int count, int& ans)
    {
        if(amount==0)
        {
            ans = min(ans, count);
            return;
        }
        //terminator
        if (index == coins.size()) return;

        for (int k = amount / coins[index]; k >= 0 && k + count < ans; k--)
        {
            //下到下一层
            //drill down
            coinChange(coins, amount - k * coins[index], index + 1, count + k, ans);
        }
    }
    int coinChange(vector<int>& coins, int amount) {
        if(amount==0)
            return 0;
        sort(rbegin(coins),rend(coins)); //排序 从大到小
        int ans=INT_MAX;
        coinChange(coins, amount, 0, 0, ans);
        return ans == INT_MAX ? -1 : ans;
    }

3.给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

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

解法:
1.若后一天比前一天价高,则为后一天减前一天即(前一天买入一天卖出)
2.若后一天比前一天价低,则不买卖
3.这里贪心算法的决策是:只加正数。

 int maxProfit(vector<int>& prices) {
        if(prices.size()==0) return 0;
        int profit=0;
        //for循环,时间复杂度为O(n) 空间为O(1)
        for(int i=0;i<prices.size()-1;i++)
        {
            if (prices[i+1]-prices[i]>0) profit += (prices[i+1]-prices[i]);
        }
        return profit;
    }

感谢阅读,欢迎大家讨论!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值