2019独角兽企业重金招聘Python工程师标准>>>
前言
贪心是人类自带的能力,贪心算法是在贪心决策上进行统筹规划的统称。
比如一道常见的算法笔试题----跳一跳:
有n个盒子排成一行,每个盒子上面有一个数字a[i],表示最多能向右跳a[i]个盒子;
小明站在左边第一个盒子,请问能否到达最右边的盒子?
比如说:[1, 2, 3, 0, 4] 可以到达第5个盒子;
[3, 2, 1, 0, 4] 无法到达第5个盒子;
我们自然而然能产生一种解法:尽可能的往右跳,看最后是否能到达;比如:站在3上就跳3下,站在2上就跳2下([1, 2, 3, 0, 4] 和[3, 2, 1, 0, 4]都无法到达)
本文即是对这种贪心决策的介绍。
正文
贪心算法基础概念
狭义的贪心算法指的是解最优化问题的一种特殊方法,解决过程中总是做出当下最好的选择,因为具有最优子结构的特点,局部最优解可以得到全局最优解;这种贪心算法是动态规划的一种特例。能用贪心解决的问题,也可以用动态规划解决。
而广义的贪心指的是一种通用的贪心策略,基于当前局面而进行贪心决策。正如上面的跳一跳
贪心算法的思考过程
贪心的思考过程类似动态规划,依旧是两步:大事化小,小事化了。
大事化小:
一个较大的问题,通过找到与子问题的重叠,把复杂的问题划分为多个小问题;
小事化了:
从小问题找到决策的核心,确定一种得到最优解的策略,比如跳一跳中的向右能到达的最远距离;
在证明局部的最优解是否可以推出全局最优解的时候,常会用到数学的证明方式。
贪心算法的具体应用
1、纸币找零问题
有1元、2元、5元、10元的纸币分别有a[1], a[2], a[3], a[4]张,要用这些纸币凑出m元,至少要用多少张纸币?
如果是动态规划:
要凑出m元,必须先凑出m-1、m-2、m-5、m-10元,我们用dp[i]表示凑出i元的最少纸币数;
有 dp[i]=min(dp[i-1], dp[i-2], dp[i-5], dp[i-10]) + 1
;
容易知道dp[1]=dp[2]=dp[5]=dp[10]=1
;
比如要凑够15元即求dp[15]的值,dp[15] = min(dp[14],dp[13],dp[10],dp[5])+1 ,d[5]==d[10]=1,所以先选择10或者5然后在选择5或10
似乎有些不对?平时我们找零钱有这么复杂吗?
从贪心算法角度出发,当m>10且我们有10元纸币,我们优先使用10元纸币,然后再是5元、2元、1元纸币。
从日常生活的经验知道,这么做是正确的。
假如我们把题目变成这样,原来的策略还能生效吗?
有1元、5元、7元的纸币分别有a[1], a[2], a[3]张,要用这些纸币凑出m元,至少要用多少张纸币?
比如说要凑出10元,如果优先使用7元纸币,则张数是4;(1+1+1+7)
但如果只使用5元纸币,则张数是2;(5+5)
在这种情况下,优先使用大额纸币是不正确的贪心选择。(但用动态规划仍能得到最优解)