查看: 3141| 回复: 2
跳转到指定楼层
上一主题 下一主题
收起左侧

[其他] 转发一个贪心算法的总结 再求点大米

🔗
九章 | 只看该作者 |倒序浏览
全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
https://zhuanlan.zhihu.com/p/57217890

求解最优化的问题常常会有一系列的步骤,而每个步骤往往会面临着选择。

而贪心算法就是这样的算法,它在每一步都做出最优解,也就是说,它总是做出局部最优解,寄希望于通过局部最优解来获得全局最优解。

我们首先举个栗子:调度竞争资源来完成任务,比如我们要利用有限的资源(如一个阶梯教室)来实现任务(举办活动),每个活动的时间不一样,分别有开始时间和就结束时间,拿如何得到最优解呢?

我们看到虽然用动态规划来求解活动选择问题,但是并不需要这么做,我们可以反复选择最早结束的活动,同时保留与此兼容的活动,重复这一过程,直到不再有剩余活动。我们选择了活动结束的最早时间,所以活动结束时间必然是单调递增的,所以我们只需要按照结束时间的单调递增顺序处理所有活动,直到活动结束,每个活动只考察一次。

定理:考虑任何非空子集问题S,a是S中最早结束的活动,所以a必然在S中某个最大兼容活动子集中。
贪心算法往往是这种自顶向下的设计,先做出一个选择,然后再求解下一个问题,而不是自底向上解出许多子问题,然后再做出选择。

在做贪心选择时,我们直接做出当前问题中看起来最优的解,而不是考虑到子问题的解,这也是贪心算法和动态规划的不同之处,在动态规划中,我们往往每一个步骤都求做一个选择,这个选择往往依赖于子问题的解。而在贪心算法中,我们总是做出当时看来最佳的选择,然后再求解剩下唯一的子问题。贪心算法做出选择时可能会依赖于之前的选择或者子问题的解,但是绝对不依赖于将来的选择或者子问题的解。就是我们前面所说的,一个动态规划问题是自底向上的,而一个贪心算法问题是自顶向下的。

下面是几道leetcode题,解法思路都是贪心算法,我用的是python:

1.分配饼干

Leetcode : 455. Assign Cookies (Easy)

Input: [1,2], [1,2,3]

Output: 2

题目描述:每个孩子都有一个满足度,每个饼干都有一个大小,只有饼干的大小大于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。

思路:因为最小的孩子最容易得到满足,因此先满足最小孩子。给一个孩子的饼干应当尽量小又能满足该孩子,这样大饼干就能拿来给满足度比较大的孩子。
假设在某次选择中,贪心策略选择给第 i 个孩子分配第 m 个饼干,并且第 i 个孩子满足度最小,第 m 个饼干为可以满足第 i 个孩子的最小饼干,利用贪心策略最终可以满足 k 个孩子。假设最优策略在这次选择中给 i 个孩子分配第 n 个饼干,并且这个饼干大于第 m 个饼干。我们发现使用第 m 个饼干去替代第 n 个饼干完全不影响后续的结果,因此不存在比贪心策略更优的策略,即贪心策略就是最优策略。
class Solution:
   def findContentChildren(self, g, s):
       """
       :type g: List[int]
       :type s: List[int]
       :rtype: int
       """
       g.sort()
       s.sort()
       i=j=res=0
       while i<len(g) and j<len(s):
           if g[i]<=s[j]:
               res += 1
               i += 1
           j += 1
       return res


2.投飞镖刺破气球

Leetcode : 452. Minimum Number of Arrows to Burst Balloons (Medium)

Input:

[[10,16], [2,8], [1,6], [7,12]]

Output:

2

一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

思路:从左往右投飞镖,并且在每次投飞镖时满足以下条件:
1.左边已经没有气球了;
2.本次投飞镖能够刺破最多的气球。
class Solution:
   def findMinArrowShots(self, points):
       """
       :type points: List[List[int]]
       :rtype: int
       """

       points.sort(key = lambda x:(x[0],x[1]))
       n = len(points)
       if n == 0:return 0
       S = points[0][0]
       E = points[0][1]
       res = 1
       for i in range(1,n):
           if E>points[i][1]:
               E = points[i][1]
           elif S<points[i][0]:
               S = points[i][0]
           if S>E:
               res += 1
               S = points[i][0]
               E = points[i][1]
       return res


3.股票的最大收益

Leetcode : 122. Best Time to Buy and Sell Stock II (Easy)

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

input: [7,1,5,3,6,4]

output: 7

思路: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。

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

对于 [a, b, c, d],如果有 a <= b <= c <= d ,那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,那么就把 prices[i] - prices[i-1] 添加加到收益中,从而在局部最优的情况下也保证全局最优。
class Solution:
   def maxProfit(self, prices):
       """
       :type prices: List[int]
       :rtype: int
       """
       profit = 0
       for i in range(len(prices)-1):
           if prices[i+1]>prices[i]:
               profit += (prices[i+1]-prices[i])
       return profit


4.种植花朵

Leetcode : 605. Can Place Flowers (Easy)

Input: flowerbed = [1,0,0,0,1], n = 1

Output: True

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。

给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表示种植了花),和一个数 n 。能否在不打破种植规则的情况下种入 n 朵花?能则返回True,不能则返回False。

示例 1:

input: flowerbed = [1,0,0,0,1], n = 1

output: True

思路:自己想吧,不难的
class Solution:
   def canPlaceFlowers(self, flowerbed, n):
       """
       :type flowerbed: List[int]
       :type n: int
       :rtype: bool
       """
       flowerbed.append(0)
       flowerbed.insert(0,0)
       num = 0
       res = 0
       for i in range(len(flowerbed)):
           if flowerbed[i] == 0:
               num += 1
           else :
               num = 0
           if num == 3:
               res += 1
               num = 1
       return res>=n


不多就这样了,希望本文能够帮到你!~!

评分

参与人数 19大米 +54 收起 理由
yeehaah + 20 感谢分享!
liming5712 + 1 给你点个赞!
海绵宝宝 + 2 很有用的信息!
pow25 + 2 给你点个赞!
iejr + 2 很有用的信息!

查看全部评分


上一篇:转二分搜索总结 并求大米
下一篇:一个类似union find题目请教
🔗
liming5712 2019-10-31 04:01:47 | 只看该作者
全局:
给你点个赞!
回复

使用道具 举报

🔗
jliu 2019-10-31 23:50:54 | 只看该作者
全局:
之前有人说现在面试都比较少考greedy了, 是真的么
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表