算法基础--贪心策略

本文主要做为本身的学习笔记,并不具有过多的指导意义。

概述

贪心算法一般用来求解最优问题node

  1. 由局部最优解到总体最优解git

    经过不断对局部最优进行操做,最终达到总体最优算法

  2. 无后效性swift

    后序操做,不会出现数据状态的回滚数组

  3. 和DP(动态规划)之间的联系bash

    不少贪心问题能够经过DP进行求解dom


最优装载问题

  1. 给出N个物体,第一个物体重量为Mi
  2. 尽可能选择最多的物品,总重不超过C

先将物品按照质量排序,而后依次放入每一个物品,直到总重量将超过C位置。ide

这里依次将剩余物品中质量最小的物品放入的过程,就是贪心的过程。学习


合并果子

一类总过程代价,取决于子过程代价的问题测试

  1. 有N堆果子,没堆果子的数量为Ai,每次能够将两堆果子合并,每次合并将消耗两堆果子总数的体力。
  2. 求最小消耗的体力
  3. 1<N<10000

首先,若是咱们什么都无论直接两两合并:总计消耗48点体力

而后,咱们尝试排序后两两合并:总计消耗44点体力

最后,咱们尝试只将当前全部数据中最小的两个进行合并:总计消耗38点体力

解法

构建一个小根堆,每次从堆顶推出两个元素合并。而且将合并都的元素追加进小根堆中便可。

具体证实的过程有必定难度,能够参考哈夫曼编码证实的过程。

以上的操做过程,也就是贪心的过程。他只保证单次合并所消耗的体力最优,而不在乎其余的数据该如何合并。

堆结构每每用来解决贪心的问题。由于贪心问题每每须要一个明确的指标,最大值或者最小值。


项目利润

输入:

cost[]:每一个项目的花费

profits[]:每一个项目的利润(纯利润)

k:最多能作k个项目

w:表示初始资金

输出:

最后能够得到的最大钱数

说明:一次只能作一个项目,且作完一个以后立刻就能得到收益,能够支持作下一个项目

  1. costprofits中的元素依次合并成一个新的节点node:
public class Node {
    public var c :Int //项目花费
    public var p :Int //项目利润
    
    public init(cost:Int,profit:Int) {
        self.c = cost
        self.p = profit
    }
}
复制代码
  1. 准备一个以项目花费构建的小根堆

将全部node依次放入

  1. 准备一个以项目利润构建的大根堆

贪心过程:

  1. 从小根堆中依次弹出堆顶元素,直到node.c>w(项目所需资金大于当前资金)

    具体代码上,将小根堆数组removeFirst,而后将arr[0]与arr[arr.count-1]位置交换。让小根堆对arr[0]位置元素向下调整便可。

  2. 将小根堆中弹出的元素放入大根堆中(大根堆中即为当前可执行的项目)

    具体代码上,将元素追加进大根堆数组末尾,并进行调整便可。

  3. 从大根堆中弹出堆顶元素,并将w += node.p(执行收益最大的项目,而且更新当前资金)

    具体代码上与第一步相似

该贪心过程总计执行k次,每一次执行都只须要关心小根堆中最小值,与大根堆中最大值便可。最后的w即为最大总资产。


会议安排

在优先的时间内安排数量最多的会议

作一张图能够直观表示过程:

咱们将蓝色表示为待安排红色表示为已安排黑色表示为不可安排

咱们能够尝试几种不一样的贪心策略

  1. 每次选择持续时间最短的安排

显然不可行

  1. 每次选择开始时间最先的

显然也不可行

  1. 每次选择开始时间最先的而且持续时间最短的来安排

因而可知该方案是能够行的

代码也很简单,只须要关心当前有效数据内开始时间晚于当前会议结束时间结束时间最先的一个数据便可。

func bestArrange(programs:[Program]) -> Int {
    program.sort("end")//根据program.end进行排序
    
    var res = 0
    var current = 0
    
    for p in programs {
        if p.current > current {  //开始时间晚于当前时间,不然做废
            res += 1
            current = p.end //开会,当前时间变成会议结束时间
        }
    }
    return res
}
复制代码

贪心策略的证实

贪心策略的数学证实一般很复杂,有能力能够去翻阅

这里推荐一种很方便的方式,对数器。

经过小样本大样本量的测试,证实贪心策略的正确性。

以排序算法的证实举例

var checkOK = true
for i in 0..<10000 {
    var arr1 = generateRandomArray(size: 5, value: 20) //获取一个长度为10,最大值为20的随机数数组
    var arr2 = arr1 //数组在swift里属于值类型,赋值动做会自动copy
    let originalArr = arr1
    arr1.sort()//必定正确的算法
    radixSort(arr: &arr2, maxDigit: 2)
    if arr1 != arr2 {
        checkOK = false
        print(originalArr)
        print(arr2)
        break
    }
}

print(checkOK ? "比对成功":"比对失败")
复制代码

对于贪心问题,可能不必定存在一个必定正确的算法。那么咱们彻底能够不去比对结果是否一致,只要贪心策略的结果永远优于默认顺序得出的结果便可。

关于对数器的介绍能够参阅另外一篇


参考资料

贪心算法

贪心算法3: 会议安排

左神牛课网算法课

相关文章
相关标签/搜索