贪心算法之剖析

miloyang
0 评论
/ /
934 阅读
/
2580 字
12 2023-10

什么是贪心?贪心就是自然智慧的算法。

介绍

比如说你在公司里面,产品经理说,我们要把充值用户服务好,他给我们充值了,我们就有现金流了,就能渡过难关,这是贪心算法。
产品经理又说,优质的客户是可以给我们推广,让更多人知道我们,从而带动用户流入,这也是贪心算法。

也就是说,贪心算法,就是你每一步做出的角色,是当前状况下看来,是最好的决策。当你做完决策后,情况会变,然后在变化的情况中,继续选下当前最好的决策,一步步的去处理,每一步都做当下最好的决策,这就是贪心算法。

我们在计算机中所说的贪心算法,难点在于你要证明当先做出的决策,是全局最优解的。如果你每一步都是最优解,那么到最后你的贪心算法是有效的,如果你某一步并非最优解,你的贪心算法就是无效的。

举个例子:
如果一个数组中,有N个大于1的正数,无序的,你如何获得最大的分数和?规则如下:

  • 第一个挑出来的数 乘 1 ,就是你的分数1。
  • 第二个挑出来的数 乘 2 ,就是你的分数2。
  • 第三个挑出来的数 乘 3 ,就是你的分数3。 然后把分数1+分数2+分数3的总和 比如[1,6,3] ....
    你如何在这样的规则下,找到最优解?,也就是最大的乘积和。

你需要把数组排序下,然后第一个数乘1,第二个数乘2,第三个数乘3等等,依次相加。比如是最优解。
也就是 1*1+2*3+3*6的组合,一定是最优解,排序后,越到后面数越大,而index也是越大,两个大数相乘,肯定是大数。

这就是简单的 贪心算法,因为每一步都是最优解,如果是 1*1+2*6*3*3,第一步贪心算法是对的,但是第二步就开始错了,所以这个贪心算法是错误的。

所以,贪心算法,并没有一个公式让你去套用,它取决于你平时的阅历和经验,还有你的数学能力。因为它难点在于证明局部最有利的标准可以得到全局最优解。

例子

字符串数组字典序问题

先介绍一个概念:
字典序:就是两个字符串,当做单词放入字典中,谁在前面谁最小。 当然,我们的规则是a-z的顺序规则。如果位数相同,则按照规则来,如果位数不相同,则短的在后面补0,0就是比a更小的数。比如两个单词,一个是abc,一个是bs,明显是abc单词比bs单词字典序最小,因为a在前面,又比如abc和abd,abc字典序最小,因为c比d小。

题目: 给定一个由字符串组成的数组,必须把所有的字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果。
例如,给定字符串数组[ac,bk,sc,ket],拼出来字典序最小的结果就是acbkketsc,当然,单词不能拆开。

func concatenateStringGreedy(strs []string) string {
    sort.Slice(strs, func(i, j int) bool {
        return strs[i]+strs[j] < strs[j]+strs[i] // 这里的每一步,都是比较两个字符串的大小
    })

    return strings.Join(strs, "")
}

会议室安排问题

题目:公司中一些项目要占用会议室,会议室不能同时容纳两个项目的宣讲,给出项目的开始世间和结束时间,要求写一个go程序来安排宣讲的会议,要求会议室进行的宣讲次数最多,返回最多的宣讲场次。
比如开会时间为:{6, 7},{8, 9},{7, 8},{7, 10},{6, 9}} ,最优的选项就是三场,{6 7} {7 8} {8 9}。

这其实是一个很典型的贪心问题,有好几种解决思路:

  • 1:按照开始时间来排序,错误
  • 2:按照开会时间最短的来,错误
  • 3:按照开会时间最长的来,错误
  • 4:按照结束时间来排序,正确。

由此可见,贪心算法就是各种模式,然后得出每一步都是最优解。

func arrange(meets []meet) {
    // 根据结束时间排序
    sort.Slice(meets, func(i, j int) bool {
        return meets[i].endTime < meets[j].endTime
    })
    fmt.Println(meets)
    fmt.Println("--------------")
    arrangeMeets := []meet{meets[0]}

    currentEnd := meets[0].endTime
    // 如果结束时间小于下一场的开始时间,则安排
    for i := 1; i < len(meets); i++ {
        if currentEnd <= meets[i].startTime {
            arrangeMeets = append(arrangeMeets, meets[i])
            currentEnd = meets[i].endTime
        }
    }
    fmt.Println(arrangeMeets)
}
人未眠
工作数十年
脚步未曾歇,学习未曾停
乍回首
路程虽丰富,知识未记录
   借此博客,与之共进步