46.贪心算法之二:做个好秘书,会议安排问题

Posted 和孩子一起学Python

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了46.贪心算法之二:做个好秘书,会议安排问题相关的知识,希望对你有一定的参考价值。



    以会议安排问题为例,进一步理解贪心算法“当前最优”的含义。




    上一篇,阿里巴巴又一次偷偷溜进了大盗们藏宝藏的山洞,这次他在山洞里看到很多箱子,箱子里装满了各种沙子状的贵重金属,比如金沙,银沙等等。

    每个箱子上也贴上了标签,写着这个箱子里宝物的总重量(不含箱子重量)和总价值。    

46.贪心算法之二:做个好秘书,会议安排问题

    如果阿里巴巴有足够多不计重量的布口袋,在小毛驴承重有限的前提下,如何拿走价值最多的宝物?

    

分析与代码实现


    这个问题的贪心策略是什么?

    很显然,每一步的“当前最好”的选择是拿单价最高的宝物。

    

    

    第1步,利用random模块,创建n个随机重量和价值的箱子。 

    

import random

s = []
n = 10
for i in range(n):
s.append([random.randint(1, 20), random.randint(5, 1000)])

print(s)


    

    第2步,对n个箱子按宝物的单价从大到小重新排序。

    比如第1步得到如下10个随机重量和价值的箱子,怎么对它们按单价重新排序呢?


46.贪心算法之二:做个好秘书,会议安排问题

     

    在《16.快速掌握Python列表类:Python列表类函数归纳总结》里,我们介绍了Python列表类的重用函数,其中sort() 函数可以对列表里的元素进行排序。

    比如 

    list1 = [10,3,18,4,9]

    调用sort() 函数之后

    list1.sort()

    列表里的元素就变成了 [3,4,9,10,18]


    但现在我们列表里的元素都是一个包含2个元素的字列表,并且需要将子列表里的2个元素相除,根据商来排序。

    Python是否提供了简易的方法呢?

    当然是有的,只需要使用下面这一行代码,就可以实现按单价从大到小排序。


s.sort(key=lambda x: x[1]/x[0], reverse=True)


    sort()函数里的  key = lambda x : x[1]/x[0]

    表示,这次的排序要指定关键字key

    这个关键字 key 的逻辑是什么呢?就是 lambda 函数

    在python里, lambda 函数也称为匿名函数,这种函数没有函数名,只有函数的内容,在上面的代码里,函数的内容就是  x : x[1]/x[0]

    其中 x 就是循环列表s时,取出来的每一个子列表,当拿到一个x时,比如[3,223],就计算 x[1]/x[0] ,也就是总价值除以总重量,得到单价。


    lambda 函数是Python一个重要的高级技巧,应用很广泛,但这里就不展开了。


    按单价排好序之后的代码就很简单了,按单价依次去取就好了。

    代码省略……



阿里巴巴的新挑战


    阿里巴巴再一次溜进了山洞,这次,他发现山洞里摆满了各种各样的古董,每个古董上也贴了一张标签

    关羽的青龙偃月刀 41公斤 100万

    诸葛亮的白羽扇    1公斤    50万

    射瞎夏侯惇的箭    0.1公斤 10万

    汪伦送李白的酒杯  0.5公斤  20万

    ……

    


46.贪心算法之二:做个好秘书,会议安排问题


    同样,假设小毛驴的承重是C公斤。

    阿里巴巴应该怎么挑选,使得拿走古董的价值最大?


    假设还是采用贪心策略,每次都拿单价最高的古董。

    假设有下面4件古董,且小毛驴的承重是15公斤。

    

46.贪心算法之二:做个好秘书,会议安排问题


    如果每次都挑选单价最高的古董

    第1件,挑古董1。累积重量2公斤,累积价值100万

    第2件,挑古董2。累积重量12公斤,累积价值300万。

    第3件,挑古董3,累积重量已经超过小毛驴的承重,放不下了。

    所以,阿里巴巴最多只能拿走价值300万的古董。


    但是,很显然,阿里巴巴可以挑选古董1、古董3、古董4。总重量14公斤,总价值320万。


    为什么贪心算法在这不适用了呢?

    因为在这个问题里,局部最优不能得到全局最优

    

    怎么求解这个问题,已经超出了“贪心算法”的范畴。我们在后面再介绍。

    像这类往口袋里装东西,在承重有限的情况下,怎么选择使得价值最大的问题,被称为“背包问题”。

    “背包问题”是数学和计算机科学领域一个非常著名的研究方向。

    “背包问题”根据假设条件的不同有多种典型的分类,比如上面的问题叫做“01背包问题”,后面我们将会介绍使用不同的算法解决各种类型的背包问题,包括如何解决“01背包问题”。


    46.贪心算法之二:做个好秘书,会议安排问题



会议安排问题


    大家都知道,老板总是很忙,每天有很多会等着老板参加,但老板又没有时间参加所有的会议,所以需要秘书来合理安排。


46.贪心算法之二:做个好秘书,会议安排问题

    

    某一天,老板对秘书说:“你安排一下,我希望今天尽可能多参加几场会议”。

    秘书看了一下如下希望老板参加的会议,她应该如何安排,才能使老板参加会议的场次最多?

    [[8,10],[9,11],[10,15],[11,14],[13,16],[14,17],[15,17],[17,18],[18,20],[16,19]]

    列表里的每一个子列表都表示一场会议

    比如第一场会议[8,10],表示这场会议开始时间是8点,结束时间是10点。


    这个问题适合用贪心算法来做吗?

    我们来分析一下:

    1.全局的问题是否可以分解成多步

        会议总是要一场一场开,所以全局问题可以分解成多步。

    2.每一步是否可以做出“当前最好”的选择

        每一步“当前最好”的选择是什么?

        是会议时间最短?

        是会议开始时间最早?

        是会议结束时间最早?


        如果一下子想不到的话,不妨用时间轴把会议表示出来。

    

46.贪心算法之二:做个好秘书,会议安排问题


    通过上面的图,就不难发现,每一步的“最优选择”应该是“结束时间最早,且和之前选择的会议时间不冲突

    

    3.每一步做出“当前最好”选择之后,是否全局的选择也是最优的。

    不严格证明了。


会议安排问题的Python实现


   如下是参考代码,不是性能最优的,但是是比较容易理解的。


    第1步:对列表按会议结束时间排序 k.sort(key = lambda x:x[1])

    第2步:把第一个会议直接加入到结果列表中。

    第3步:双重循环,每次子循环都从剩余会议中挑出“结束时间最早,且和之前选择的会议时间不冲突的会议。” 

 

k = [[8,10],[9,11],[10,15],[11,14],[13,16],[14,17],[15,17],[17,18],[18,20],[16,19]]

k.sort(key=lambda x:x[1])

result = []
result.append(k.pop(0))

while len(k) > 0:
pos = -1
minV = k[-1][1] + 1
for i in range(len(k)):
if k[i][0] < result[-1][1]:
continue
elif
k[i][1] < minV:
pos = i
minV = k[i][0]

if pos > -1:
result.append(k.pop(pos))
else:
break




股票买卖时机问题


    46.贪心算法之二:做个好秘书,会议安排问题

    

    如果问沉迷炒股的人,最希望获得什么超能力。

    答案一定是:预知未来的能力。


    假设你拥有了预知未来的能力,来看下面2道题。


    题目1:

    给定一个列表,如[7,1,5,3,6,4]。表示的是某只股票每天的价格

    如果只允许你买卖一次,什么时候买入,什么时候卖出才能收益最大。

    

    题目2


    还是某只股票,[7,1,5,3,6,4]

    如果允许你买卖多次,什么时候买入,什么时候卖出才能收益最大。



    往期文章 https://dwz.cn/gBg1UPmk
    或关注公众号,点“原创文章”






扫描二维码

获取更多精彩

少儿编程


以上是关于46.贪心算法之二:做个好秘书,会议安排问题的主要内容,如果未能解决你的问题,请参考以下文章

算法设计与分析贪心算法--活动安排问题

算法笔记(0002) - 贪心算法活动安排问题

会场安排问题(贪心算法)

活动安排问题-贪心算法

活动安排问题-贪心算法

贪心算法之安排最多会议问题