贪心算法

贪心算法,是在对问题求解时,总是做出在当前看来是最好的选择,即只考虑某种意义上的局部最优解。
对于某种意义的思考,应该是考虑无后忧性,即局部最优不影响整体最优。贪心算法一般都需要证明我们找到的解就是答案要求的最优解,证明方法通常是替换法。
即假设存在某个最优解,证明我们用贪心算法找到的解和这个最优解是一样的或者效果一样。

(ps:大概过一周更新贪婪算法的应用)

如:leetcode #45跳跃游戏ii,#55跳跃游戏,#135分发糖果

(1)#45跳跃游戏ii
1>题目:
给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

说明:

假设你总是可以到达数组的最后一个位置。

2>思路:

考虑局部最优,我从第一步开始跳跃,最远可以跳a[0]步,那么在a[1]到a[a[0]]这些数组元素里循环,如果能找到某个元素a[i],
这个a[i]相比其他元素跳的更远或者一样远,这就是我们要找到的局部最优,因为其他元素能够跳到的地方,a[i]也能够到达。

证明:假设答案要求的最少步数是k步,对应序列为a[i0],a[i1],...,a[ik],我们用贪心算法找到的最优解序列是a[j0],a[j1],...,a[jk],a[j(k+1)],...
a[i0]=a[j0],根据我们前面讲的思路,利用贪心算法得到的a[j1]...,一定有a[j1]>a[i1],a[j2]>a[i2],...
(因为我们的a[j]序列是当前元素代表的步数相比其他位置元素能到达更远地方的,比如思路里的a[0],a[i],...这样往下写)
因此,a[jk]>a[ik],既然a[ik]能一次到达末尾,那么a[jk]也一定可以,因此用贪心算法找到的也是最优解。

3>参考代码:

class Solution {
public:
    int jump(vector<int>& nums) {
        int ret = 0;
        int steps = nums[0];
        int i = 0;
        int pos = 0;
        int a = 0;
        int lenth = nums.size();
        if (lenth == 1)
            return 0;
        while (nums[pos] < lenth - pos - 1)
        {
            int tmp = 0;
            steps = nums[pos];
            for (i = 1; i <= steps; ++i)
            {
                if (steps + nums[a + i] + i > tmp)
                {
                    tmp = steps + nums[a + i] + i;
                    pos = a + i;
                }
            }
            ++ret;
            a = pos;
        }
        ret += 1;
        return ret;
    }
};

(2)#55跳跃游戏

1>题目:
给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 从位置 0 到 1 跳 1 步, 然后跳 3 步到达最后一个位置。

示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

2>思路:

可以和跳跃游戏ii的做法一样,用一样的方法,如果我在循环的过程中,停在了数组元素为0的位置,说明肯定跳不到终点,
反之则可以达到终点,其中注意对边界讨论,比如只有一个元素,且这个元素就是0,这也是算作可以到终点的。

3>参考代码:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int steps = nums[0];
        int i = 0;
        int pos = 0;
        int a = 0;
        int lenth = nums.size();
        if (lenth == 1)
            return true;
        if (nums[0] == 0)
            return false;
        while (nums[pos] < lenth - pos - 1)
        {
            int tmp = 0;
            steps = nums[pos];
            for (i = 1; i <= steps; ++i)
            {
                if (steps + nums[a + i] + i > tmp)
                {
                    tmp = steps + nums[a + i] + i;
                    pos = a + i;
                }
            }
            if (nums[pos] == 0)
                return false;
            a = pos;
        }
        return true;
    }
};

4>另一种思路(另一种意义下的局部最优解)

我们可以逆向遍历数组,从数组末尾开始,判断前一个位置a是否能到达"当前位置"b(开始时当前位置就是末位置),如果可以,更新"当前位置"为a,
再判断新的前一个位置能否到达"当前位置",如果0(数组第一个元素)也被更新成了"当前位置,说明可以到终点,反之则不可以。

参考代码:

class Solution {
public:
    bool canJump(vector<int>& nums) {
    
        int i = 0,lenth=nums.size();
        int pos = lenth-1;
        for (i = lenth - 1; i >= 0; --i)
        {
            if (i + nums[i] >= pos)
                pos = i;
        }
        return pos == 0;
    }
};

(3)#135分发糖果
1>题目:
老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?

示例 1:
输入: [1,0,2]
输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

示例 2:

输入: [1,2,2]
输出: 4
解释: 你可以分别给这三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这已满足上述两个条件。

2>思路:

这里的局部最优,就是把当前位置的元素与两端的元素做比较,如果大于,则要保证分配相应多的糖果。
但是不能直接在一个循环里面进行两次比较,比如位置1,2,3,4,分数:1<2,2>3>4,如果一次进行两次比较的话,
第一次:假设位置1分配的糖果为1,则位置2分配糖果数2,位置3分配1.
第二次:由于位置4分配糖果1,所以位置3分配糖果至少是2,与位置2分配的糖果相等。
出现问题的根本原因是当我在和下一个位置进行比较的时候,下一个位置糖果数目还没有更新,必须等到下一个位置糖果数目更新后才能比较
(即应该先让下一个位置和下下个位置进行比较,这个任务由逆向循环完成)
因此,可以考虑把两次比较分开进行,用两次循环,第一次从左往右,第二次从右往左,这样就不会有上述问题了。

3>参考代码:

class Solution {
public:
    int candy(vector<int>& ratings) {
    int lenth = ratings.size();
        vector<int>ret(lenth,1);
        int i = 0;
        for (i = 1; i < lenth; ++i)
        {
            if (ratings[i] > ratings[i - 1]&&ret[i]<=ret[i-1])
            {
                ret[i] = ret[i - 1] + 1;
            }
        }
        for (i = lenth - 2; i >= 0; --i)
        {
            if (ratings[i] > ratings[i + 1]&&ret[i]<=ret[i+1])
            {
                ret[i] = ret[i + 1] + 1;
            }
        }
        return accumulate(ret.begin(), ret.end(), 0);
    }
};

(这个方法来自于博客:https://blog.csdn.net/qq_21567767/article/details/81987689

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 按要求补齐数组 给定一个已排序的正整数数组 nums,和一个正整数 n 。从 [1, n] 区间内选取任意个数字补...
    一酷到底阅读 351评论 0 0
  • 前言 贪心是人类自带的能力,贪心算法是在贪心决策上进行统筹规划的统称。 比如一道常见的算法笔试题----跳一跳: ...
    落影loyinglin阅读 41,763评论 11 84
  • 贪心算法,又称贪婪算法。 1、在对问题求解时,总是做出在当前看来最好的选择。即贪心算法不从整体最优上加以考虑。 2...
    李威威阅读 919评论 0 1
  • Jump Game 跳跃游戏Given an array of non-negative integers, yo...
    Phoebe_Liu阅读 1,093评论 0 1
  • 所有的一切都是真的 不论喜怒哀乐 亦或是悲欢离合 它们深入我们的骨髓 组成了自己 让我们知道自己是谁 我要去哪里 ...
    JJ_Lin阅读 90评论 0 0