vlambda博客
学习文章列表

【每周算法】贪心算法(Greedy Algorithm)

不知不觉就要开学了,俺又来水推送了


前言



        贪心是人类自带的能力,贪心算法是在贪心决策上进行统筹规划的统称。

比如一道常见的算法题----跳一跳:


有n个盒子排成一行,每个盒子上面有一个数字a[i],表示最多能向右跳a[i]个盒子;
小明站在左边第一个盒子,请问能否到达最右边的盒子?
比如说:[1, 2, 3, 0, 4] 可以到达第5个盒子;
[3, 2, 1, 0, 4] 无法到达第5个盒子;


我们自然而然能产生一种解法:尽可能的往右跳,看最后是否能到达。
本文即是对这种贪心决策的介绍。




基础概念




        狭义的贪心算法指的是解最优化问题的一种特殊方法,解决过程中总是做出当下最好的选择,因为具有最优子结构的特点,局部最优解可以得到全局最优解;这种贪心算法是动态规划的一种特例。能用贪心解决的问题,也可以用动态规划解决。

        而广义的贪心指的是一种通用的贪心策略,基于当前局面而进行贪心决策。以跳一跳的题目为例:
我们发现的题目的核心在于向右能到达的最远距离,我们用maxRight来表示;
此时有一种贪心的策略:从第1个盒子开始向右遍历,对于每个经过的盒子,不断更新maxRight的值。




思考过程





        贪心的思考过程类似动态规划,依旧是两步:大事化小,小事化了。
大事化小:
        一个较大的问题,通过找到与子问题的重叠,把复杂的问题划分为多个小问题;
小事化了:
        从小问题找到决策的核心,确定一种得到最优解的策略,比如跳一跳中的向右能到达的最远距离;


        在证明局部的最优解是否可以推出全局最优解的时候,常会用到数学的证明方式。




具体应用




纸币找零问题

        有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;
根据以上递推方程和初始化信息,可以容易推出dp[1~m]的所有值。


似乎有些不对?平时我们找零钱有这么复杂吗?
从贪心算法角度出发,当m>10且我们有10元纸币,我们优先使用10元纸币,然后再是5元、2元、1元纸币。
从日常生活的经验知道,这么做是正确的,但是为什么?


假如我们把题目变成这样,原来的策略还能生效吗?

有1元、5元、7元的纸币分别有a[1], a[2], a[3]张,要用这些纸币凑出m元,至少要用多少张纸币?


接下来我们来分析这种策略:
已知对于m元纸币,1,2,5元纸币使用了a,b,c张,我们有a+2b+5c=m;假设存在一种情况,1、2、5元纸币使用数是x,y,z张,使用了更少的5元纸币(z<c),且纸币张数更少(x+y+z<a+b+c),即是用更少5元纸币得到最优解。
我们令k=5*(c-z),k元纸币需要floor(k/2)张2元纸币,k%2张1元纸币;(因为如果有2张1元纸币,可以使用1张2元纸币来替代,故而1元纸币只能是0张或者1张)
容易知道,减少(c-z)张5元纸币,需要增加floor(5*(c-z)/2)张2元纸币和(5*(c-z))%2张纸币,而这使得x+y+z必然大于a+b+c。


由此我们知道不可能存在使用更少5元纸币的更优解。
所以优先使用大额纸币是一种正确的贪心选择。

对于1、5、7元纸币,比如说要凑出10元,如果优先使用7元纸币,则张数是4;(1+1+1+7)
但如果只使用5元纸币,则张数是2;(5+5)
在这种情况下,优先使用大额纸币是不正确的贪心选择。(但用动态规划仍能得到最优解)


服务器任务安排问题


服务器有n个任务要执行,每个任务有开始时间Si秒和结束时间Ti秒,同一时间只能执行一个任务。
问如何安排任务,使得在时间m内尽可能多的完成任务。


如果是动态规划:
前i秒的完成的任务数,可以由前面1~i-1秒的任务完成数推过来。
我们用dp[i]表示前i秒能完成的任务数;
在计算前i秒能完成的任务数时,对于第j个任务,我们有两种决策:
1、不执行这个任务,那么dp[i]没有变化;
2、执行这个任务,那么必须腾出来(Sj, Tj)这段时间,那么dp[i] = max(dp[i], dp[ S[j] ] ) + 1;
比如说对于任务j如果是第5秒开始第10秒结束,如果i>=10,那么有dp[i]=max(dp[i], dp[5] + 1);(相当于把第5秒到第i秒的时间分配给任务j)


再考虑贪心的策略,现实生活中人们是如何安排这种多任务的事情?我换一种描述方式:


小明在学校兼职,小明一天兼职的时间有10个小时;
现在有多个兼职岗位,每个岗位有个开始时间和结束时间,小明同一时间只能做一个兼职;
问小明每天最多能做几份兼职?


我们自然而然会想到一个策略:先把结束时间早的兼职给做了!
为什么?
因为先做完这个结束时间早的,能留出更多的时间做其他兼职。
我们天生具备了这种优化决策的能力。




总结





贪心的学习过程,就是对自己的思考进行优化。

是把握已有信息,进行最优化决策。


这里是给大佬们准备的一大堆题:

https://www.luogu.org/problem/list?tag=7

这期就到这里,我们开学再见ヾ( ̄▽ ̄)Bye~Bye~