首页 > 其他分享 >(31/60)贪心理论、分发饼干、摆动序列、最大子序和

(31/60)贪心理论、分发饼干、摆动序列、最大子序和

时间:2024-02-28 22:25:50浏览次数:24  
标签:preDiff nums int 31 60 最优 size 子序 贪心

贪心的一天,头好晕

理论基础

什么是贪心

每次选择都采取局部最优,最终得到全局最优。

(一定是每个阶段都采取局部最优,能够推出全局最优的,如果得不到全局最优就不用贪心法)

套路

没有套路。

但是可以判断用不用贪心:

通过数学归纳/反证法的方式,模拟一下看看能不能局部最优->整体最优。

(一般情况下就是反证法就行了,看看能不能找到反例,得不到整体最优的)

贪心法的一般过程

贪心算法一般分为如下四步:

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

这个四步其实过于理论化了,我们平时在做贪心类的题目 很难去按照这四步去思考,真是有点“鸡肋”。

做题的时候,只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了。


分发饼干

leetcode:455. 分发饼干

贪心法

思路

排序后,遍历每个孩子,找最小能满足胃口的饼干,最后结果就是尽可能多地满足孩子。

复杂度分析

时间复杂度:O(NlogN)。主要花在排序上。

空间复杂度:O(1)。

注意点

  1. 双指针就够了,不用起两层循环。

代码实现

双指针法(暴力法两层循环太辣眼睛了就不放上来了)

class Solution {
public:
    // 胃口和饼干排序
    // 遍历孩子,对每个孩子遍历饼干,找可能的最小的饼干
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int count = 0;
        // 不用套两个循环,可以双指针
        int gIndex = 0;
        int sIndex = 0;
        while(gIndex != g.size() && sIndex != s.size()){
            if(s[sIndex] >= g[gIndex]){ // 能满足 下一个孩子 下一块饼干
                count++;
                gIndex++;
                sIndex++;
            }else{  // 满足不了,还是这个孩子,下一块饼干
                sIndex++;
            }
        }

        return count;
    }
};

摆动序列

leetcode:376. 摆动序列

回溯法

思路

理论上应该是可行的,但是我写不出来,在面对输入[1,2,3,4,5,6,7,8,9]时,不能正确返回[2]。

代码实现

class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
public:
    // 试下回溯暴力枚举
    void backtracking(vector<int>& nums,int begin,int sign){
        if(path.size() > 0) result.push_back(path);

        for(int i = begin;i < nums.size();i++){
            // path元素不足两个时,直接放入元素
            if(path.size() <= 1) path.push_back(nums[i]);
            else{   // 两个元素及以上时,检查符号是否相反
                if( (nums[i] - path.back()) * sign > 0){
                    path.push_back(nums[i]);
                    backtracking(nums,i + 1,-sign);
                    path.pop_back();
                }
            }
        }
    }
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size() <= 2) return nums.size();
        int sign = 0;
        if(nums[1] - nums[0] > 0 ) sign = 1;
        else if(nums[1] - nums[0] < 0) sign = -1;
        backtracking(nums,0,-sign);
        
        int ret = 0;
        for(vector<int> vec : result){
            if(vec.size() > ret) ret = vec.size();
        }

        return ret;
    }
};

贪心法

思路

出现峰值result++,且规定最右边有一个峰值。

可能的三种情况:

  1. 上下坡出现峰值 。preDiff > 0 && postDiff < 0 || preDiff < 0 && postDiff > 0
  2. 上下坡出现平坡。这种也要算,所以preDiff >= 0 && postDiff < 0 || preDiff <= 0 && postDiff > 0
  3. 单调上下出现平坡。这种不能算,preDiff只在出现峰值时更新即可

综上,条件就是preDiff >= 0 && postDiff < 0 || preDiff <= 0 && postDiff > 0,而且每次只计算postDiff,如果是峰值才更新preDiff

(本质上来说,就是把坡压缩掉)

复杂度分析

时间复杂度:O(N)。一轮遍历。

空间复杂度:O(1)。

注意点

  1. nums有0个、1个元素时直接返回大小,但2个及以上就要在循环里了(2个元素时要判断是否是相等的两个元素)。

代码实现

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        if(nums.size() <= 1) return nums.size();
        int preDiff = 0;    // 前差值
        int postDiff = 0;   // 后差值
        int result = 1; // 规定最右边有一个峰值
        for(int i = 0;i < nums.size() - 1;i++ ){
            postDiff = nums[i+1] - nums[i];
            // 如果出现峰值,就result++并更新preDiff
            // 这步压缩平坡,只有出现峰值才会进入操作
            if( (preDiff >= 0 && postDiff < 0 ) || (preDiff <= 0 && postDiff > 0) ){
                result++;
                preDiff = postDiff;
            }
        }

        return result;
    }
};

最大子数组和

leetcode:53. 最大子数组和

贪心法

思路

局部最优:和为负数时,不论加甚麽都只会拖累整体累和。因此连续和为负数的时候,抛弃当前累和,从下一个元素开始计算累和。

整体最优:得到最大连续子数组的累和。

复杂度分析

时间复杂度:O(N)。遍历一轮。

空间复杂度:O(1)。

注意点

  1. 求最大和,要在遍历过程中尝试向上更新最大值(最大值可能出现在中间)。

代码实现

class Solution {
public:
    // 连续和为负数时,从当前元素开始计数
    int maxSubArray(vector<int>& nums) {
        int sum = 0;
        int maxSum = INT32_MIN;
        for(int i = 0;i < nums.size();i++){
            sum += nums[i];
            maxSum = sum > maxSum ? sum : maxSum;
            if(sum < 0){
                sum = 0;
            }
        }

        return maxSum;
    }
};

标签:preDiff,nums,int,31,60,最优,size,子序,贪心
From: https://www.cnblogs.com/tazdingo/p/18042091

相关文章

  • 1.31
    <!--轮播图渲染--><swiperclass="swiper_container"><!--轮播图具体部件--><swiper-item><viewclass="item">A</view></swiper-item><swiper-item><viewclass=&q......
  • P10160 [DTCPC 2024] Ultra 题解
    【题目描述】给你一个\(01\)序列,你可以进行如下操作若干次(或零次):将序列中形如\(101\cdots01\)的一个子串(即\(1(01)^k\),\(k\ge1\))替换成等长的\(010\cdots10\)(即\(0(10)^k\))。你要操作使得\(1\)的个数尽可能少,输出最少的\(1\)的个数。【思路】一开始看到这道题......
  • 绿联DX4600部署新版frp内网穿透
    绿联DX4600部署新版frp内网穿透操作流程(配置流程)1.服务端安装frps2.客户端安装frpc3.验证是否成功操作步骤(配置步骤)一、服务端安装frps1.安装服务端需要一台带有IPV4公网的云服务器。2.云服务器使用1panel面板创建docker2.1.使用ssh工具连接远程服务器,输入以下命令:#根据......
  • cf1606e-solution
    CF1606ESolutionlink考虑dp。注意到这个题造成的伤害与剩余人数有关,每次消灭的人数又与剩余人的血量最大值有关:设\(dp_{i,j}\)表示剩下\(i\)个人中血量最大值为\(j\)的方案数。显然当\(i-1>=j\)时一次伤害就可以杀光所有人,于是这时\(dp_{i,j}=j^i-(j-1)^i\)(只需让......
  • cURL error 60: SSL certificate problem: unable to get local issuer certifica 解
    cURLerror60:SSLcertificateproblem:unabletogetlocalissuercertifica解决 无法获取本地颁发者证书 Windows版本1.到https://curl.haxx.se/ca/cacert.pem下载证书文件cacert.pem,将其保存到PHP安装路径下。2.编辑php.ini文件,删除curl.cainfo配置项前......
  • linux基本知识汇总2(系统编程) 60000字汇总
    /////////////进程/任务--task任何启动并运行程序的行为,都是由操作系统帮助我们将程序转换成进程--进程:完成特定的任务进程控制块:PCB(win)/task_struct(linux)--结构体结点/内核数据结构--提取了进程的所有属性task_struct是PCB的一种在Linux中描述进程的结构体叫......
  • [ABC331G] Collect Them All 题解
    洛谷传送门AT传送门\(\textbf{Statement.}\)有\(M\)种颜色,用\(1\simM\)编号,每次抽奖抽中第\(i\)种颜色的概率为\(\frac{c_i}{N}\),其中\(\sumc_i=N\),求抽中每种颜色至少一次的期望次数。\(1\leM\leN\le2\times10^5\)。\(\textbf{Solution.}\)发现直接求不好......
  • P5605 小 A 与两位神仙 题解
    推销博客P5605小A与两位神仙题意给定\(x\)、\(y\)和\(m\),其中\(m=p^n,n\in\mathbb{N+},p\ge3\),问同余方程\(x^a\equivy\pmodm\)是否有非负整数解。分析前置芝士Pollard_rho原根化简对这种指数型的同余方程是很难解决的,我们要先把它转化成线性的同余方......
  • AT_abc317_f 题解
    调了一小时结果发现爆longlong了。考虑数位dp,具体来说,设计状态\(dp_{i,r_1,r_2,r_3,mx_1,mx_2,mx3_,c_1,c_2,c_3}\)表示当前考虑到第\(i\)位,\(x_1,x_2,x_3\)模\(a_1,a_2,a_3\)等于\(r_1,r_2,r_3\)三个数是否达到\(n\)的上界以及是否全部是\(0\)。然后从高到低枚......
  • 《梦断代码》1.31
     《梦断代码》的第三章节中,作者描述了主人公对于代码的热爱和执着。代码不仅是一种工具,更是一种信仰,一种对于美的追求。主人公对于编程的热情和执着让人感动,他不断地学习和探索,不断地追求完美的代码。这一章节让人深刻地感受到了主人公对于代码的热爱和追求,也让人对于编程这门......