贪心算法入门(455.860.134)

贪心算法的定义


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

贪心算法一般按如下步骤进行:  

①建立数学模型来描述问题。

②把求解的问题分成若干个子问题。

③对每个子问题求解,得到子问题的局部最优解。

④把子问题的解局部最优解合成原来解问题的一个解。

例题

1.分发饼干

解题思路
要尽可能满足越多数量的孩子,可以将孩子们的胃口值 g[i] 和 饼干的尺寸 s[j] 进行排序。这里是按照从大到小的顺序进行排序。

然后让尺寸最大的饼干 maxSizeCookies 去满足胃口值最大(最贪心)mostGreedy的孩子,如果能满足(让他满意),则留给下一个次贪心孩子的饼干也将是当前看来最大的饼干,用尺寸次大的饼干去满足他,依次类推;如果不能满足,那么胃口值最大(最贪心)的孩子,对不起,无法满足你的胃口让你开心,此时用尺寸最大的饼干 maxSizeCookies 去满足胃口值次大(次贪心)的孩子,尽量满足次贪心的孩子,依次类推。

举例
假设 g = [1,2], s = [1,2,3]。

1.将 g 和 s 从大到小排序,得到 g = [2,1], s = [3,2,1];

2.用尺寸最大的饼干 s[0] = 3 去满足最贪心(胃口值最大)g[0] = 2 的孩子 ,能满足;

3.用尺寸次大的饼干 s[2] = 2 去满足次贪心(胃口值次大)g[0] = 1 的孩子 ,能满足;

满足孩子的最大数值为 2。

代码如下

int comp(const void *a, const void *b) {
    return *(int *)b - *(int *)a;
}

int findContentChildren(int* g, int gSize, int* s, int sSize){
    qsort(g, gSize, sizeof(int), comp);
    qsort(s, sSize, sizeof(int), comp);

    int res = 0;
    int i = 0, j = 0;
    while (i < gSize && j < sSize) {
        /* 尺寸最大的饼干能满足最贪心的孩子,用尺寸次大的去尝试满足次贪心的孩子 */
        if (s[j] >= g[i]) {
            j++;
            i++;            
            res++;
        /* 尺寸最大的饼干不能满足最贪心的孩子,则去尝试满足次贪心的孩子 */
        } else {
            i++;
        }
    }
    return res;
}

2.柠檬水找零

  • 简单模拟,按贪心算法,优先找零大面额
  • 每10元,需要找零1张5元
  • 每20元,需要找零1张10元和1张5元,或者3张5元

代码如下

bool lemonadeChange(int* bills, int billsSize){
    int fives = 0, tens = 0;
    for (int i = 0; i < billsSize; i++) {
        int bill = bills[i];
        if (bill == 5) {
            fives++;
        }
        else if (bill == 10) {
            if (fives == 0) {
                return false;
            }
            tens++;
            fives--;
        }
        else {
            if (fives >= 1 && tens >= 1) {
                tens--;
                fives--;
            }
            else if (fives >= 3) {
                fives -= 3;
            }
            else {
                return false;
            }
        }
    }
    return true;
}

3.加油站

能够走完的条件有两个
1.获得的汽油的量大于总油耗
2.车从i站能开到i+1

设 三个变量total、cur、ans
分别记记录 可获得的总油量-总油耗、当前油耗情况、出发位置

在遍历数组过程中当cur小于0时,表明从ans位置出发到i位置为
死路,无法通行,此时应将cur置零,ans改为i+1
如果最后total>= 0 即ans位置出发可以环行一周,则返回ans,
否则返回 -1.

代码如下

int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){
    // total记录可获得的总油量-总油耗, cur记录当前油耗情况, ans记录出发位置
    int total = 0, cur = 0, ans = 0;
    for(int i=0;i<gasSize;i++){
        total += (gas[i] - cost[i]);
        cur += (gas[i] - cost[i]);
        if(cur<0){                          // 油不够开到i站
            cur = 0;                        // cur置零,在新位置重新开始计算油耗情况
            ans = i + 1;                    // 将起始位置改成i+1
        }
    }return total<0? -1:ans;                // 如果获得的汽油的量小于总油耗,则无法环
                                            // 行一周返回 -1;反之返回ans
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值