贪心算法例子

一、含义

贪心算法是指在对问题求解时,总是作出在当前看来是最好的选择。也就是不从整体最优解上加以考虑,只考虑局部最优解。

二、算法思路

  • 建立数学模型描述问题
  • 把求解的问题分成若干个子问题
  • 对每个子问题求解,得到子问题的局部最优解
  • 把子问题的解局部最优解合成原来解问题的一个解

贪心算法采用自顶向下,以迭代的方法做出相继的贪心选择,每做依次贪心选择,就将所求问题简化为一个规模更小的子问题。通过每一步贪心选择,可得到问题的一个最优解。虽然每一步上都要保证能获得局部最优解,但由此产生的全局解有时不一定是最优的,所以贪心算法不用回溯。

三、算法特性

  1. 有一个以最优方式来解决的问题。为了构造问题的解决方法,有一个候选对象集合:比如不同面值的硬币。
  2. 随着算法的进行,将积累起其他两个集合:一个包含已经被考虑过并被选出的候选对象,另一个包含已经被考虑过但被丢弃的候选对象。
  3. 有一个函数来检查一个候选对象的集合是否提供了问题的解答。该函数不考虑此时的解决方法是否最优。
  4. 还有一个函数检查是否一个候选对象的集合是否是可行的,即是否可能往该集合上添加更多的候选对象以获得一个解。和上一个函数一样,此时不考虑解决方法的最优性。
  5. 选择函数可以指出哪一个剩余的候选对象最有希望构成问题的解。
  6. 最后,目标函数给出解的值。

四、使用条件

1、贪心选择性质:一个问题的最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。

2、最优子结构性质:当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。

五、例题

分发糖果:n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。需要按照以下要求,给这些孩子分发糖果:

  • 每个孩子至少分配到1个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的最少糖果数量。

方法一:两次遍历

相邻两个孩子评分更高的孩子会获得更多的糖果 这句话可以拆分为两个规则分别处理:

(1)左规则:当ratings[i-1]<ratings[i]时,i号学生的糖果数量将比i-1号孩子的糖果数量多。

(2)右规则:当ratings[i]>ratings[i+1]时,i号学生的糖果数量将比i+1号孩子的糖果数量多。

遍历该数组两次,处理出每一个孩子分别满足左右规则时,最少需要被分得的糖果数量。最终每个人分的的糖果数量即为这两个数量的最大值。

/**
 * @param {number[]} ratings
 * @return {number}
 */
var candy = function(ratings) {
    let res=0,arr=[]
    for(let i=0;i<ratings.length;i++){
        if(ratings[i]>ratings[i-1]){
            arr[i]=arr[i-1]+1
        }else{
            arr[i]=1
        }
    }
    let right=0
    for(let i=ratings.length-1;i>-1;i--){
        if(ratings[i]>ratings[i+1]){
            right++
        }else{
            right=1
        }
        res+=Math.max(arr[i],right)
    }
    return res
};

方法二:常数空间遍历

糖果要尽量少给,从1开始累计,每次要么仍然是1,要么比相邻的同学多给1个。从左到右枚举每一个同学,记前一个同学分得的糖果数量为pre:

  • 如果当前同学比上一个同学评分高,说明我们就在最近的递增序列中,直接分配给该同学pre+1;否则就在递减序列中,直接分配给当前同学一个糖果,并把该同学所在的递减序列中所有的同学都再多分配一个糖果,以保证糖果数量还是满足条件。
  • 无需显式地额外分配糖果,只需记录当前的递减序列长度,即可知道需要额外分配的糖果数量。
  • 同时注意当当前的递减序列长度和上一个递增序列等长时,需要把最近的递增序列的最后一个同学也并进递减序列中。

这样,只要记录当前递减序列的长度dec,最近的递增序列长度inc和前一个同学分得的糖果数量pre即可。

var candy = function(ratings) {
    const n = ratings.length;
    let ret = 1;
    let inc = 1, dec = 0, pre = 1;

    for (let i = 1; i < n; i++) {
        if (ratings[i] >= ratings[i - 1]) {
            dec = 0;
            if (ratings[i] === ratings[i - 1]) pre = 1;
            else pre++;
            ret += pre;
            inc = pre;
        } else {
            dec++;
            if (dec === inc) {
                dec++;
            }
            ret += dec;
            pre = 1;
        }
    }
    return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值