C++算法 —— 贪心(4)

news2024/9/23 21:32:07

文章目录

  • 1、分发饼干
  • 2、最优除法
  • 3、跳跃游戏Ⅱ
  • 4、跳跃游戏Ⅰ
  • 5、加油站
  • 6、单调递增的数字
  • 7、坏了的计算器


1、分发饼干

455. 分发饼干

在这里插入图片描述
其实看完这个题会发现,如果给定的两个数组不排序的话会非常难受,所以无论怎样,先排序。接下来需要比较两个数组的值,可以用双指针来指向。两个数组的两个元素比较时,和之前有相同的思路,如果满足条件,那么后面的元素都比这个元素大,肯定也满足,但为了满足更多次的条件,所以就选用最小的那个值;如果不满足条件,这里就跳过去,找下一个更大的元素去看看能否满足条件。这也就是贪心思想。

    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());
        int res = 0, m = g.size(), n = s.size();
        for(int i = 0, j = 0; i < m && j < n; ++i, ++j)
        {
            while(j < n && s[j] < g[i]) ++j;
            if(j < n) res++;
        }
        return res;
    }

2、最优除法

553. 最优除法

在这里插入图片描述
无论怎样,假设abcdefg7个数,a / b / c / d / e / f,整个式子就是一个分式,a一定在分子,b一定在分母。对于贪心来说,让分子变大,让分母变小,就是最优解。这道题来看,其实应当让分子变大就是它的最优解,所以接下来就要让分子变大。让分子变大的办法就是在把b ~ f都放到一个括号里,这样就变成了 a * c * d * e * f / b。

    string optimalDivision(vector<int>& nums) {
        int n = nums.size();
        if(n == 1) return to_string(nums[0]);
        if(n == 2) return to_string(nums[0]) + "/" + to_string(nums[1]);
        string res = to_string(nums[0]) + "/(" + to_string(nums[1]);
        for(int i = 2; i < n; ++i)
        {
            res += "/" + to_string(nums[i]);
        }
        res += ")";
        return res;
    }

3、跳跃游戏Ⅱ

45. 跳跃游戏 II

在这里插入图片描述
这道题意思就是如果是[2, 3, 1, 5, 4],那么在0下标位置时最多可以跳2步到1这个位置。

这道题可以用动规,以i位置为结尾,遍历一遍前面所有的元素,如果能从某个位置跳过来,那就选那个位置,而那个位置存储了到它的最小跳跃数,然后+1即可,但这样是n ^ 2的时间复杂度,思路并不行。

这道题的思路可以是一个类似层序遍历的过程。假设一个数组[2, 3, 1, 1, 4, 2, 6, 7, 1, 5, 8],从0下标开始,是2,我们能够确定跳到3或1这个点,也就是第一次选定起点后确定了下一次起跳的左端点和右端点;接着,3可以跳到1,1,4,而1这里,就加上贪心,因为3跳得远,且它一定比1要至少1,所以1能跳到的,3一定能跳到的,所以这里就只考虑大的数字,跳的区间为114,但是重叠了,重叠部分是2下标处的1,所以把这部分去掉,只看1和4,1就是左端点,4就是右端点;接着从4走,能到2671,这时候14和2671没有重叠的,所有不会划掉一部分,2就是左端点,1就是右端点。这样的过程就是选定了一个点后,就能确定下一次的左端点和右端点,所以很像层序遍历。

这个思路的时间复杂度是O(N)。

    int jump(vector<int>& nums) {
        int left = 0, right = 0, maxPos = 0, n = nums.size(), res = 0;
        while(left <= right)//防止跳不到n - 1位置
        {
            if(maxPos >= n - 1)//先判断是否已经能跳到最后一个位置
                return res;
            for(int i = left; i <= right; ++i)
            {
                maxPos = max(maxPos, nums[i] + i);
            }
            left = right + 1;
            right = maxPos;
            res++;
        }
        return -1;
    }

4、跳跃游戏Ⅰ

55. 跳跃游戏

在这里插入图片描述
先看跳跃游戏Ⅱ。

其实就是改两处

    bool canJump(vector<int>& nums) {
        int left = 0, right = 0, maxPos = 0, n = nums.size();
        while(left <= right)//防止跳不到n - 1位置
        {
            if(maxPos >= n - 1)//先判断是否已经能跳到最后一个位置
                return true;
            for(int i = left; i <= right; ++i)
            {
                maxPos = max(maxPos, nums[i] + i);
            }
            left = right + 1;
            right = maxPos;
        }
        return false;
    }

5、加油站

134. 加油站

在这里插入图片描述
在这里插入图片描述
按照这个题的思路来看,两个数组要同时看,这时不如作为一个数组,因为真正需要的是差。gas和cost两个数组每每对应,用gas的减cost的,比如例1,就得到[-2, -2, -2, 3, 3]。最简单的办法就是暴力解法,依次枚举所有起点,模拟加油的流程。

实际上,这道题可以在暴力解法上改进而得到。差值的数组不需要创建出来,不过下面还是看差值数组。用i表示下标,然后用一个step变量,表示走多少步,比如走0步就还是原地,走1步就到下一个位置,不过要%上数组大小,这样就不会越界了。

    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        for(int i = 0; i < n; i++)
        {
            int rest = 0;
            for(int step = 0; step < n; step++)
            {
                int index = (i + step) % n;
                rest = rest + gas[index] - cost[index];
                if(rest < 0) break;
            }
            if(rest >= 0) return i;
        }
        return -1;
    }

不过这样肯定超出时间限制。现在基于这个来优化。假设差值数组是abcdefg,从a走到f就不能走了,说明a + b + c + d + e + f < 0,暴力解法就会从b再来一遍,但这样明显做了无用功。上面的式子小于0,那么去掉a,还是小于0,所以就不用管这些了,直接从g位置再出发。这样平均时间复杂度就是O(N)了。

    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();
        for(int i = 0; i < n; i++)
        {
            int rest = 0;
            int step = 0;
            for(; step < n; step++)
            {
                int index = (i + step) % n;
                rest = rest + gas[index] - cost[index];
                if(rest < 0) break;
            }
            if(rest >= 0) return i;
            i = i + step;
        }
        return -1;
    }

6、单调递增的数字

738. 单调递增的数字

在这里插入图片描述
当然最简单的方法就是暴力枚举,从这个数字到0,判断每一个数字是否是单调递增,找到的第一个就是结果。不过重点在于如何判断单调递增。

对于一个数字,如果想对每一位做一些判断,转换成字符串就好。另一个经典操作就是模10再除10,就能从个位开始拿到每一位。

这个暴力解法的时间复杂度是O(nlogn),取一个数字的每一位的时间复杂度是logn。

但这里不用暴力解法,要用贪心,不过这更像找规律。

从头开始判断,如果发现了不是递增,那要对字符串如何操作?比如123454367,到了5这个位置就不能继续了,但因为要找更小的数字,那么4367不能改为6367。我们可以修改5,把它减1,然后后面的数字全变成9,就是要求的数字。但这里还有问题,如果是连续的几个5之后有个4呢?这样就得把第一个5改成4,后面全变成9。

    int monotoneIncreasingDigits(int n) {
        string s = to_string(n);
        int i = 0, m = s.size();
        while(i + 1 < m && s[i] <= s[i + 1]) ++i;
        if(i + 1 == m) return n;
        while(i - 1 >= 0 && s[i] == s[i - 1]) --i;
        s[i]--;
        for(int j = i + 1; j < m; ++j) s[j] = '9';
        return stoi(s);
    }

7、坏了的计算器

991. 坏了的计算器

在这里插入图片描述
对于小于目标的数,那么乘2会更快地接近目标,但是也有不是最优解的,比如6和目标10。

这道题适合逆着思考,把操作变成除2和+1。
这个题没有小数,所以能除2的只能是偶数,那么遇到奇数的话就只能+1。偶数可以除2,可以+1。把目标值变成原始值,原始值则变成目标值。假设原有的目标值是end,原始值是begin。

针对偶数,如果end <= begin,也就是目标值更小,那么遇到偶数就得+1。如果end > begin,奇数还是只能+1,而偶数,经过证明,其实是先除更优。

    int brokenCalc(int startValue, int target) {
        //正难则反 + 贪心
        int res = 0;
        while(target > startValue)
        {
            if(target % 2 == 0) target /= 2;
            else target += 1;
            res++;
        }
        return res + startValue - target;//目标值变成小于原始值了,奇偶数都是+1,所以就是加上差值。
    }

结束。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1248696.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯每日一题2023.11.24

题目描述 #include <stdio.h> #define N 100int connected(int* m, int p, int q) {return m[p]m[q]? 1 : 0; }void link(int* m, int p, int q) {int i;if(connected(m,p,q)) return;int pID m[p];int qID m[q];for(i0; i<N; i) ________________________________…

软文写作如何布局?媒介盒子分享三大类型

好的软文需要有清晰的结构和流畅的语言&#xff0c;让读者能够很快理解和接受文案的内容&#xff0c;因此在写文案之前&#xff0c;需要先列出思路和框架&#xff0c;明确文案的主题和重点&#xff0c;选择合适的语言和表达方式。让文案更加生动易懂&#xff0c;下面就让媒介盒…

yo!这里是c++11重点新增特性介绍

目录 前言 列表初始化 { }初始化 initializer_list类 类型推导 auto decltype 范围for 右值引用与移动语义 左值引用和右值引用 移动语义 1.移动构造 2.移动赋值 3.stl容器相关更新 右值引用和万能引用 完美转发 关键字 default delete final和override …

数组基础知识

数组基础&#xff08;不定时更新&#xff09; 数组基础 数组基础 &#xff08;1&#xff09;数组是存放在连续内存空间上的相同类型数据的集合。数组可以方便的通过下标索引的方式获取到下标下对应的数据。数组下标都是从0开始的。数组内存空间的地址是连续的。 &#xff08;…

python-选择排序

选择排序是一种简单直观的排序算法&#xff0c;它的基本思想是每一轮选择未排序部分的最小元素&#xff0c;然后将其放到已排序部分的末尾。这个过程持续进行&#xff0c;直到整个数组排序完成。(重点&#xff1a;通过位置找元素) 以下是选择排序的详细步骤和 Python 实现&…

element ui 上传组件实现手动上传

首先需要给上传组件增加http-request属性&#xff0c;这个方法中可以获取到文件&#xff0c;并按照自己的方式进行上传。 <el-uploadreffileUploadaction#:http-requesthttpRequest:on-preview"handlePreview":on-remove"handleRemove":limit"1&q…

SpringBoot3核心原理

SpringBoot3核心原理 事件和监听器 生命周期监听 场景&#xff1a;监听应用的生命周期 可以通过下面步骤自定义SpringApplicationRunListener来监听事件。 ①、编写SpringApplicationRunListener实现类 ②、在META-INF/spring.factories中配置org.springframework.boot.Sprin…

接口测试:轻松掌握基础知识,快速提升测试技能!

1.client端和server端 开始接口测试之前&#xff0c;首先搞清楚client端与server端是什么&#xff0c;区别。 web前端&#xff0c;顾名思义&#xff0c;指用户可以直观操作和看到的界面&#xff0c;包括web页面的结构&#xff0c;web的外观视觉表现及web层面的交互实现。 web后…

Python---函数的参数类型

位置参数 理论上&#xff0c;在函数定义时&#xff0c;我们可以为其定义多个参数。但是在函数调用时&#xff0c;我们也应该传递多个参数&#xff0c;正常情况&#xff0c;其要一一对应。 相关链接&#xff1a;Python---函数的作用&#xff0c;定义&#xff0c;使用步骤&…

jQuery 第十一章(表单验证插件推荐)

文章目录 前言jValidateZebra FormjQuery.validValValidityValidForm BuilderForm ValidatorProgressionformvalidationjQuery Validation PluginjQuery Validation EnginejQuery ValidateValidarium后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&…

单链表的反转?太细了哥们!细到离谱!

单链表的反转&#xff08;面试常出&#xff09;&#xff1a; ​ 单链表的反转&#xff0c;可以通过很多种方法实现。包括迭代法&#xff0c;递归法&#xff0c; 迭代法&#xff1a; 定义三个指针&#xff1a;prev、current和next&#xff0c;它们分别表示前一个节点、当前节点…

Excel动态选择某一行/列的最后一个数据

选择列的最后一个数据&#xff1a; 以A列为例&#xff0c;使用&#xff1a; LOOKUP(1,0/(A:A<>""),A:A)选择行的最后一个数据&#xff1a; 以第3行为例&#xff0c;使用&#xff1a; LOOKUP(1,0/(3:3<>""),3:3)示例程序 列最后一个数据&a…

IO口速度影响了什么?

我们在初学单片机的时候都知道单片机GPIO的作用是巨大的&#xff0c;在配置GPIO的时候&#xff0c;结构体初始化里有一个选项是配置输入输出速度的&#xff0c;对于这个速度输出是必须要配置的&#xff0c;输入没有明令说明需不需要配置。 这个速度对于学习过32单片机的都应该知…

python——第十三天

uuid 是通用唯一识别码&#xff08;Universally Unique identifier&#xff09;的缩写 UUID是一个128比特的数值 uuid模块&#xff1a; 获取一个128位&#xff08;比特&#xff09;的永不重复的数字&#xff0c;当然我们使用的时候会转换为32个的字符串 impor uuud uui…

js粒子效果(二)

效果: 代码: <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Particle Animation</title><…

C#,《小白学程序》第七课:列表(List)其一,编制《高铁车次信息表》

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…

【算法专题】滑动窗口—无重复字符的最长子串

力扣题目链接&#xff1a;无重复字符的最长子串 一、题目解析 二、算法原理 解法一&#xff1a;暴力解法&#xff08;时间复杂度最坏&#xff1a;O(N)&#xff09; 从每一个位置开始往后枚举&#xff0c;在往后寻找无重复最长子串时&#xff0c;可以利用哈希表来统计字符出现…

2023年【汽车驾驶员(中级)】最新解析及汽车驾驶员(中级)试题及解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年汽车驾驶员&#xff08;中级&#xff09;最新解析为正在备考汽车驾驶员&#xff08;中级&#xff09;操作证的学员准备的理论考试专题&#xff0c;每个月更新的汽车驾驶员&#xff08;中级&#xff09;试题及解…

vue3 for循环创建的多个e-form 添加校验

v-for 创建 ref <el-form :model"item" :rules"state.rules" :ref"el > getRiskSpreadRef(el, index)" ></el-form>// 定义ref list const riskSpreadRefList ref<HTMLElement[]>([]);// ref存到数组 const getRiskSpread…

Error running Tomcat8: Address localhost:1099 is already in use 错误解决

摘要: 有时候运行web项目的时候会遇到 Error running Tomcat8: Address localhost:1099 is already in use 的错误&#xff0c;导致web项目无法运行。这篇 blog 介绍了解决办法。 有时候运行web项目的时候会遇到 Error running Tomcat8: Address localhost:1099 is already in …