贪心算法

1、算法描述:

贪心算法是指在对问题求解时,总是做出在当前看来最好的选择,不从总体最优上加以考虑,只是局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。

贪心算法可以认为是动态规划算法的一个特例,如果满足每做出一个局部最优解,得到的最终结果都是全局最优,那么就可以使用贪心算法对动态规划进行优化,降低时间复杂度。

例如:有100张人民币,拿出10张,是拿出的总额最大。每次拿出剩余人民币中的最大值(局部最优),最后得到的一定是总额最大的情况(全局最优)。

但是并不是所有的问题都满足这个条件,所以不是所有的问题都可以用贪心算法解决。

2、例题分析:

①两地调度https://leetcode-cn.com/problems/two-city-scheduling/

公司计划面试 2N 人。第 i 人飞往 A 市的费用为 costs[i][0],飞往 B 市的费用为 costs[i][1]。返回将每个人都飞到某座城市的最低费用,要求每个城市都有 N 人抵达。

例如:

输入:[[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 A 市,费用为 10。
第二个人去 A 市,费用为 30。
第三个人去 B 市,费用为 50。
第四个人去 B 市,费用为 20。

最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。

分析:

举个例子:

a这个人去A:100,去B:110,然后b这个人去A:10,去B:50。

由此可知a去A比B便宜10,而b去A比B便宜40。所以选择便宜的多的,即a去B,b去A。

所以将去两地的费用差作为贪心因子。

如果按照A-B的差从小到大排序,则对于前N个人,去A比去B便宜的多,所以去A地;同理,后N个人去B地。

代码如下:

class Solution:
    def twoCitySchedCost(self, costs: List[List[int]]) -> int:      
        # 若去A地能节省更多的费用,则去A,否则去B
        # 比如:a这个人去A:100,去B:110.然后b这个人去A:10,去B:50。
        # 可知a去A比B便宜10.而b去A比B便宜40。所以选择便宜的多的,即a去B,b去A。
        # 所以根据差值从小到大排序
        # 前一半人去A地,后一半人去B地
        costs.sort(key = lambda x : x[0] - x[1])
        res = 0
        n = len(costs) // 2
        for i in range(n):
            # 前一半去A的费用加上后一半去B的费用
            res += costs[i][0] + costs[i + n][1]
        return res

②无重叠区间https://leetcode-cn.com/problems/non-overlapping-intervals/

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

  • 可以认为区间的终点总是大于它的起点。
  • 区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
例如:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

分析:

作为区间调度问题,此题就是求最多有几个不重叠的区间。

如果贪心因子选择区间的开始节点,那么会遇到有的区间开始的早,结束得很晚,就会错过中间的一些小区间,此时得到的结果就不是最优解。

如果选择区间结束节点作为贪心因子,那么在它结束节点之后的区间是最大的,可选择的区间个数也是最多的,所以最后可以得到最多的不重叠区间。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        # 贪心算法
        if len(intervals)==0:
            return 0
        # 按照区间右侧升序排序
        # 如果后一个区间的开始大于等于当前区间的结束,就不重叠
        intervals = sorted(intervals,key = lambda x:x[1])
        res = 1     # 不重叠区间个数      
        end = intervals[0][1]   # 第一个区间的结束节点
        # 遍历每个区间
        for x in intervals:
            start = x[0]
            if end <= start:
                res += 1
                end = x[1]
        return len(intervals)-res

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值