学习贪心算法提升篇

前言

    最近贪心算法苦恼很久,这个可恶的局部最优解,真是伤透我心啊。所以我决定好好的全面了解他,对自己在贪心这块不会再出现错误,没错,我就是这么贪🤩🤩。

贪心算法是什么

    贪心算法(又称贪婪算法)是指:在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。(摘自百度百科
    划重点,不从整体最优上加以考虑,而是局部最优解。那么带来什么问题了呢?局部最优解和整体最优解的区别我们用两个小例子来说明一下:

第一个demo:
    这有面值分别为1,5和11单位的硬币,希望找回总额为15单位的硬币,同时拿到的硬币数量最少。在贪心算法眼里:先拿一张最大的11,因为剩余的单位不足以支撑5单位,便需要再找4个1单位面值的硬币,共找回5个硬币。
    但最优的解答应是3个5单位面值的硬币。


第二个demo:

咱们以leedcode的礼物的最大值为例:

    在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
[
[1,3,1],
[1,5,1],
[4,2,1]
]

    当时我第一眼便用了贪心,因为简单啊,一下子就想到了,下面我来说说我当时的思路:每次找到最大的那一条边,走到最后不就是最大了么,然后我欻欻地写了个递归,从第一个数到下一个数,哪个大到达哪个位置,但是我意识到一个问题,这样是到不了最后位置的;就像下图
在这里插入图片描述
那如果我给他设定最后位置在右下角,只能向下、右呢?

那这就不是最大值了,最大值肯定是有15的,所以这题只能用动态规划做。
借用一下K神的图
在这里插入图片描述
每个节点只与其上方、左方有关,从第一个行开始向右到底,算出一行最大值。接下来每一行看其左边和上边哪一个与自己相加为最大值,第2行第1列便是其上面1加上本身值,得到2,;接下来到第2列,左边2,4与自己相加得到最大值的是4…由此求得结果。
在这里插入图片描述
    因此,我们可以用一句话来概括贪心算法:一个非常精明但是格局没有打开的算法。

贪心算法的应用

    所以说贪心算法只有在某些特定的场景下才能使用。贪心的基本思路:从问题的某一个初始解出发逐步逼近给定的目标,以尽可能快的地求得更好的解。当达到算法中的某一步不能再继续前进时,算法停止。
直接上题

738. 单调递增的数字
    当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈单调递增 。

简单理解一下,把n拆分成字符
1、前一个字符要<=当前字符
2、同时要小于n。
很明显的贪心问题,用98为例,

n=98     
输出=89
n = 1234321
输出 = 1233999

局部最优:由此可以看出不满足1条件的地方str[i-1]-=1,同时str[i]=9
即需要两个位置变换,前一个数不满足1条件便持续-1,后面的数在尽可能大的情况下要变换为9;只要找到不满足1的,就要替换为9。

1234321中4>3,然后4->3;3->9;123399
全局最优:得到小于等于N的最大单调递增的整数

class Solution:
    def monotoneIncreasingDigits(self, n: int) -> int:
        l = list(str(n))
        #n=['1', '2', '3', '4', '3', '2', '1']
        for i in range(len(l)-1,0,-1):
            if int(l[i]) < int(l[i-1]):
                l[i-1] = str(int(l[i-1]) - 1)
                l[i:] = '9' * (len(l) - i) 
        return int("".join(l)) 

再来一个剑指offer中的题:
剪绳子
     给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

这个题需要不断分解,

切分方案乘积结论
2=1+11×1=12 不切分
3=1+21×2=23 不切分
4=2+2=1+32×2=4>1×3=34 和 2 等价,且左边更优(特例)
5=2+3=1+42 × 3 = 6 > 1×2×2=45 应切分为 2+3更大
6=3+3=2+2+23×3=9>2×2×2=86 应切分为 3+3 ,进而推出 3 比 2 更优
7=3+2+23×2×2=12可以转换为1-6的方案
8=2+2+2+2=3+3+2=16<18可以转换为1-6的方案

得出一个结论:大部分情况下3越多越好(疯狂贪3),需要推导公式看这

class Solution:
    def cuttingRope(self, n: int) -> int:
        if n <= 3: return n - 1
        m, z = n // 3, n % 3
        if z == 0: return int(math.pow(3, m))
        if z == 1: return int(math.pow(3, m - 1) * 4)
        return int(math.pow(3, m) * 2)

总结

贪心算法只有在特定情况下才可以使用,总结一下贪心的核心思想:当面临处境的时候只考虑当前情况下最优解,然后从局部去解决全局问题(前提是全局问题可以由局部问题解决)
先分析->判断是否可用贪心->while or 规律完成答题

力扣题汇总

力扣贪心汇总,冲

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Love And Program

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值