leetcode题解(贪心算法)

定义

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。 也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

通常贪心算法的代码会非常短而且思路也非常的简单,但贪心算法真正的难点在于确定我们当前的问题确实可以使用贪心算法。

leetcode 455. 分发饼干

paste image

解题思路

这是一道简单的贪心算法问题。我们尝试将最大的饼干给最贪心的小朋友,如果可以满足最贪心的小朋友,我们留给下一个次贪心的小朋友的饼干也是当前看来最大的。如果最大的饼干都无法满足最贪心的小朋友,那么就表明无法满足这个小朋友。

    class Solution {
    public:
        int findContentChildren(vector<int>& g, vector<int>& s) {
    
            sort(g.begin(), g.end(), greater<int>());
            sort(s.begin(), s.end(), greater<int>());
    
            int gi = 0, si = 0;
            int res = 0;
            while( gi < g.size() && si < s.size() ){
    
                if( s[si] >= g[gi] ){
                    res ++;
                    si ++;
                    gi ++;
                } else {
                    gi ++;
                }
            }
    
            return res;
        }
    };
    
复制代码

贪心算法通常和排序是分不开的,如果题目给出数组没有排序,我们就需要自己进行排序。


贪心算法与动态规划的关系

paste image

动态规划方法

解题思路

我们可以首先想一下暴力解法:找出所有子区间的组合,之后判断它是否重叠。O((2^n)*n)
先要排序,方便判断不重叠。

这道题很像最长上升子序列我们首先使用动态规划解决这道题:

实现

    
    
    // Definition for an interval.
    struct Interval {
        int start;
        int end;
        Interval() : start(0), end(0) {}
        Interval(int s, int e) : start(s), end(e) {}
    };
    
    bool compare(const Interval &a, const Interval &b){
    
        if( a.start != b.start )
            return a.start < b.start;
        return a.end < b.end;
    }
    
    // 动态规划
    class Solution {
    public:
        int eraseOverlapIntervals(vector<Interval>& intervals) {
    
            if( intervals.size() == 0 ) {
                return 0;
            }
    
            sort(intervals.begin(), intervals.end(), compare);
    
            // memo[i]表示以intervals[i]为结尾的区间能构成的最长不重叠区间序列
            vector<int> memo( intervals.size() , 1 );
            for( int i = 1 ; i < intervals.size() ; i ++ ) {
                // memo[i]
                for( int j = 0 ; j < i ; j ++ ) {
                    if( intervals[i].start >= intervals[j].end ) {
                        memo[i] = max( memo[i] , 1 + memo[j] );
                    }
                }
            }
    
            int res = 0;
            for( int i = 0 ; i < memo.size() ; i ++ ) {
                res = max( res , memo[i] );
            }
    
            return intervals.size() - res;
        }
    };
    
复制代码

贪心算法解决

解题思路

注意:每次选择中,每个区间的结尾很重要。
结尾越小,留给了后面越大的空间,后面越有可能容纳更多区间。
贪心算法:按照区间的结尾排序,每次选择结尾最早的,且和前一个区间不重叠的区间。

实现


    // Definition for an interval.
    struct Interval {
        int start;
        int end;
        Interval() : start(0), end(0) {}
        Interval(int s, int e) : start(s), end(e) {}
    };
    
    bool compare(const Interval &a, const Interval &b){
        if( a.end != b.end )
            return a.end < b.end;
        return a.start < b.start;
    }
    
    // 贪心算法
    class Solution {
    public:
        int eraseOverlapIntervals(vector<Interval>& intervals) {
    
            if( intervals.size() == 0 ) {
                return 0;
            }
    
            sort(intervals.begin(), intervals.end(), compare);
    
            int res = 1;
            int pre = 0;
            for( int i = 1 ; i < intervals.size() ; i ++ ) {
                if( intervals[i].start >= intervals[pre].end ){
                    res ++;
                    pre = i;
                }
            }
    
            return intervals.size() -  res;
        }
    };
    
复制代码

贪心选择性质的证明

  • 贪心算法为A;最优算法为O;发现A完全能替代O,且不影响求出最优解。
  • 如果无法使用贪心算法,举出反例即可

-------------------------华丽的分割线--------------------

看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

个人博客番茄技术小栈掘金主页

想了解更多,欢迎关注我的微信公众号:番茄技术小栈

番茄技术小栈

猜你喜欢

转载自juejin.im/post/5b4480cef265da0f990d4088