【贪心算法】C++解决回文串、增减字符串匹配、分发饼干、跳跃游戏、加油站问题

1. 前言

贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题,其核心思想是通过局部最优解逐步推导出全局最优解。

在贪心算法中,我们并不总是考虑到未来可能发生的情况,而是只关注当前的最优选择。这种贪心选择性质使得贪心算法特别适合解决那些具有最优子结构性质的问题,即局部最优解能够推导出全局最优解的问题。

贪心算法的基本思路可以总结为以下几步:

  1. 确定问题的最优子结构:问题的最优解可以通过子问题的最优解逐步推导得到。
  2. 构造贪心选择:在每一步都做出当前状态下的最优选择,即局部最优解。
  3. 证明贪心选择性质:证明每一步的贪心选择都是最优的,能够推导出全局最优解。

需要注意的是,贪心算法并不适用于所有的问题,因为并非所有问题都具有最优子结构性质。在某些情况下,贪心算法得到的结果可能并不是全局最优解,而只是一个较好的解。因此,在应用贪心算法时,需要仔细分析问题的特性,以确定贪心算法是否适用于该问题。

2. 算法题

2.1_最长回文串

在这里插入图片描述

题意分析

  • 题目要求找到字符串的 字符可以组成的最长回文串的长长度
  • 对于回文串,只要出现次数是偶数,就可以加上,最多只能出现一个单个字符(中间位)

思路

  • 由于只需要长度而不需要实际的串,我们可以利用哈希表和一个结果变量ret
  • hash统计各字符的出现次数:
    • ret直接加上所有字符的最高偶数次,最后如果s仍有元素,返回ret+1,否则ret就是最终结果

代码

class Solution {
public:
    int longestPalindrome(string s) {
        // 数组代替哈希
        int hash[127] = { 0 };
        for(char ch : s) hash[ch]++;
        // 统计结果
        int ret = 0;
        for(int x : hash)
            ret += x / 2 * 2; // 先加上所有偶数位(比如7个a,就加上6个)
        
        // 如果原始字符串的长度比计算出的偶数部分长度还要长,说明存在至少一个字符出现奇数次,因此需要额外再加上一个奇数长度。
        return ret < s.size() ? ret + 1 : ret;
    }
};

2.2_增减字符串匹配

在这里插入图片描述

题意分析

  • 根据题目,重点在于如何分配元素,使序列在满足当前比较关系的同时,不影响后面的分配

思路

  • 遍历字符串,当前字符为’I’插入最小的元素(保证当前元素一定小于后面)
  • 当前字符为’D’插入最大的元素(保证当前元素一定大于后面)
    代码
class Solution {
public:
    vector<int> diStringMatch(string s) {
        int left = 0, right = s.size();
        vector<int> ret;
        for(char ch : s)
        {
            if(ch == 'I')
                ret.push_back(left++);
            else
                ret.push_back(right--);
        }
        ret.push_back(left); // 加上最后一位

        return ret;
    }
};

2.3_分发饼干

在这里插入图片描述
题意分析

  • 要求尽可能使更多的孩子满足,注意每个孩子只能吃一块饼干,有了这点我们就可以利用贪心,给每个孩子吃能满足自己的最小的饼干,如何做到?——排序

思路

  • 排序两数组,利用两指针:如果孩子的胃口小于饼干大小,就可以吃,记录结果,指针右移,反之找更大的饼干
  • 由于进行了排序,只要当前饼干不能满足当前孩子,自然更不能满足后面的孩子

代码

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        // 排序数组
        sort(g.begin(), g.end());
        sort(s.begin(), s.end());

        int ret = 0, i = 0, j = 0; // 两数组的指针
        while(i < g.size() && j < s.size())
        {
            if(g[i] <= s[j])
                i++, j++, ret++;
            else
                j++;
        }
        
        return ret;
    }
};

2.4_跳跃游戏 II

在这里插入图片描述

题意分析

  • 本题目像是 跳台阶 的变体:重点是 可以跳跃的距离根据当前数组元素的值决定
  • 题目要求找到最后一个位置的最小跳跃次数,即我们跳跃要尽可能的远。
  • 这里我们使用贪心,要尽可能的跳远:(当然这道题也可以使用动态规划解题)

思路

  • 我们可以利用两个指针,分别标记当前可以跳跃的左右区间范围
  • left <= right,进行循环:
    • 每次遍历left到right 找最大的跳跃距离max(maxPos, nums[i] + i)
    • 后根据maxPos更新下一次可以跳跃到的区间

代码

class Solution {
public:
    int jump(vector<int>& nums) {
        // 类似层序遍历
        int left = 0, right = 0, ret = 0, n = nums.size(), maxPos = 0;
        while(left <= right)
        {
            // 跳到了最后
            if(maxPos >= n - 1) return ret;
			
			// 本次跳跃可以到的位置
            for(int i = left; i <= right; ++i)
                maxPos = max(maxPos, nums[i] + i); // 更新 maxPos 为当前位置能够到达的最远位置 

            left = right + 1, right = maxPos; // 下一次跳跃的范围
            ret++;
        }
        // 特殊情况,没到达最后
        return -1;
    }
};

顺便贴出动态规划解法(dp):

int jump(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, INT_MAX); // 初始化动态规划数组,初始值为无穷大
        dp[0] = 0; // 起始位置不需要跳跃

        for (int i = 1; i < n; ++i) {
            for (int j = 0; j < i; ++j) {
                if (j + nums[j] >= i) { // 如果位置 j 能够跳到位置 i
                    dp[i] = min(dp[i], dp[j] + 1); // 更新跳到位置 i 的最小跳跃次数
                }
            }
        }

        return dp[n - 1]; // 返回跳到最后一个位置的最小跳跃次数
    }

2.5_跳跃游戏

在这里插入图片描述
题意分析

  • 对于跳跃游戏Ⅰ, 只需要判断是否可以跳到最后一个下标:
  • 跟随上一题的思路使用贪心

思路

  • 跟随上一题的思路,当max >= n - 1时,返回true即可

代码

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int left = 0, right = 0, maxPos = 0, n = nums.size();
        while(left <= right)
        {
            // 跳到了最后一个位置
            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;
    }
};

2.6_加油站

在这里插入图片描述
题意分析

  • 题目要求从某个加油站出发,绕行一圈回到起点,使得途中不会因为油量不足而无法到达下一个加油站
  • 我们可以对这一过程进行模拟

思路

  • 遍历gas数组,对每一个起点进行枚举:
  • 从起点位置向后移动,每次计算剩余油量(利用循环),如果剩余<0,则该起点不合适,继续遍历
  • 直到出现走过一轮后,剩余油量依然>=0,此时返回下标

代码

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n = gas.size();

        for(int i = 0; i < n; ++i)
        {
            int remain = 0, step = 0;
            for( ; step < n; ++step)
            {
                int index = (i + step) % n; // index: 走step步后的下标
                remain += gas[index] - cost[index];
                if(remain < 0) break; // 无法到达下一站
            }
            
            if(remain >= 0) // 绕行了一圈
                return i;
            i += step; // 跳过已经遍历过的
        }

        return -1;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Centos7网络故障,开机之后连不上网ens33mtu 1500 qdisc noop state DOWN group default qlen 1000

说明 这是Linux系统网络接口的信息&#xff0c;其中"mtu 1500"表示最大传输单元大小为1500字节&#xff0c;“qdisc noop”表示没有设置特殊的队列算法&#xff0c;“state down”表示该接口当前处于关闭状态&#xff0c;“group default”表示该接口属于“default”…

Java设计模式:享元模式实现高效对象共享与内存优化(十一)

码到三十五 &#xff1a; 个人主页 目录 一、引言二、享元设计模式的概念1. 对象状态的划分2. 共享机制 三、享元设计模式的组成四、享元设计模式的工作原理五、享元模式的使用六、享元设计模式的优点和适用场景结语 [参见]&#xff1a; Java设计模式&#xff1a;核心概述&…

企业如何打造通证经济生态闭环详解(下)

一、原始账户&#xff1a;用户注册即生成【原始账户】【托管账户】。 原始账户用于存储用户所获取的通证积分&#xff0c;原始账户的公钥与私钥由用户所有&#xff0c;安全、私密、去中心化。 通过原始账户&#xff0c;用户可进行转账、收款的点对点传输&#xff0c;并可查看…

I2C协议详解

文章目录 概念工作模式 原理工作原理工作流程IIC协议的关键特点IIC通信过程 优点与缺点优点缺点 概念 IIC&#xff08;Inter-Integrated Circuit&#xff09;协议&#xff0c;也常被称为TWI&#xff08;Two-Wire Interface&#xff09;协议&#xff0c;是一种用于短距离通信的…

CyberDAO M级共识交流会·西安站圆满落幕:共筑Web3美好未来

CyberDAO M级共识交流会于2024年5月28日在西安隆重举行&#xff0c;这是一场CyberDAO精英汇聚的盛会&#xff0c;以同心共筑&#xff0c;志在必达为主题口号与DAO精英携手并进&#xff0c;共筑CyberDAO美好宏图。CyberDAO的使命是降低WEB3的门槛&#xff0c;帮助用户轻松抓住行…

STM32的时钟介绍

目录 前言1. 简介1.1 时钟是用来做什么的1.2 时钟产生的方式 2. 时钟树的组成2.1 时钟源2.1.1 内部时钟2.1.2 外部时钟 2.2 PLL锁相环2.3 SYSCLK2.4 AHB和HCLK2.5 APB和PCLK2.6 总结 3. STM32时钟的使用步骤4.我的疑问4.1 使用MSI和HSI有什么区别吗&#xff1f;4.2 MSI的频率为…

【ai】livekit:Agents 4: livekit-plugins-openai和LiveKit Plugins Silero安装与分析

先提高下性能然后本文 继续按照 上一篇【ai】livekit:Agents 3 : pythonsdk和livekit-agent的可编辑模式下的安装构建 livekit-gent的插件。pycharm 工程 配置Microsoft Defender 排除列表 livekit-plugins-openai 本地安装

window自动启动bat文件

开机自动开启远程桌面&#xff0c; WinR 执行netplwiz 命令进入设置&#xff1b;取消勾选&#xff0c;可选择所需用户&#xff0c;点击应用&#xff0c;输入远程的密码即可 开机自动开启远程桌面&#xff0c; WinR 执行netplwiz 命令进入设置&#xff1b;取消勾选&#xff0…

新书推荐—华为HCIA路由交换技术实战

新书推荐—华为HCIA路由交换技术实战 由HCIE认证讲师、技术能手、ICT大赛优秀指导教师、教学名师、国家规划教材作者联袂编撰&#xff0c;让学习不再是“硬”茬&#xff0c;而是“嗨”起来&#xff01; 《华为HCIA路由交换技术实战》 作者黄君羡组编正月十六工作室书号978-7-12…

Linux 编译器gcc/g++使用

gcc/g同理 编译器运行过程 1. 预处理&#xff08;进行宏替换) gcc -E a.c -o a.i 预处理后还是c语言 -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面 告诉gcc&#xff0c;从现在开始进行程序的翻译&#xff0c;将预处理工作做完停下 2. 编译&#x…

ETLCloud中如何执行SQL脚本

SQL脚本 在数据库管理与数据分析的广阔领域中&#xff0c;SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;脚本扮演着举足轻重的角色。作为一门专为关系型数据库设计的编程语言&#xff0c;SQL不仅能够执行数据的检索、更新、插入及删除等基…

python列表元素操作与函数应用详解

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、列表元素加一的实现方法 示例代码 二、列表生成式的简化操作 三、列表反转函数revers…

解密网络流量监控:优化IT运维的利器

引言&#xff1a; 在当今数字化时代&#xff0c;网络流量监控是维护网络稳定与业务连续性的关键。作为一名资深网络工程师&#xff0c;我将分享一些关于网络流量监控的重要知识&#xff0c;并探讨如何在IT运维中运用这一工具优化网络性能&#xff0c;确保业务的顺畅进行。 1. 网…

jquery---ajax方法示例

ajax方法 $.ajax({name:value, name:value, ... }) ajax方法有一个参数&#xff0c;一定长度的对象&#xff0c;内部指定了ajax的请求地址和格式&#xff0c;方式等等&#xff0c;它可以有以下的属性和值 示例 这里展示了一个简单的get请求图片url的实例 let data; let url…

压力测试JMeter

压力测试JMeter 1 下载JMeter1.1 测试计划1.2 JMeter Address Already in use 错误解决1.3 java 内存模型1.4 jconsole与jvisualvm1.5 优化方向1.6 Nginx动静分离 1 下载JMeter 官网地址&#xff1a;https://jmeter.apache.org/download_jmeter.cgi 运行apache-jmeter-5.6.3\…

Linux 驱动设备匹配过程

一、Linux 驱动-总线-设备模型 1、驱动分层 Linux内核需要兼容多个平台&#xff0c;不同平台的寄存器设计不同导致操作方法不同&#xff0c;故内核提出分层思想&#xff0c;抽象出与硬件无关的软件层作为核心层来管理下层驱动&#xff0c;各厂商根据自己的硬件编写驱动…

手把手从0到1教你做STM32+FreeRTOS智能家居--第10篇之ASR-PRO语音识别模块

前言 先看实验效果&#xff0c;通过ASR-PRO语音智能识别控制模块&#xff0c;来控制STM32单片机实现对应的控制功能。因为后台好多小伙伴私信问用的是什么语音模块&#xff0c;并且很少在网上看到如何使用此模块相关的文章&#xff0c;所以我将会在本篇文章详细介绍一下此模块…

国际数字影像产业园|科技与文创产品创意集市,共筑创新文化新高地

5月29日&#xff0c;为进一步增强园区与企业之间粘性&#xff0c;不断激发企业的创新活力&#xff0c;园区举办了“数媒大厦科技与文创产品创意集市活动”。本次活动由成都树莓信息技术有限公司主办&#xff0c;成都目莓商业管理有限公司、树莓科技&#xff08;成都&#xff09…

成都欣丰洪泰文化传媒有限公司助力品牌快速崛起

在当今数字化浪潮汹涌的时代&#xff0c;电商行业作为新经济的代表&#xff0c;正以其独特的魅力和无限的潜力&#xff0c;引领着商业模式的创新与变革。在这个充满机遇与挑战的领域里&#xff0c;成都欣丰洪泰文化传媒有限公司凭借其专业的电商服务能力和前瞻性的战略眼光&…

【Paddle】Inplace相关问题:反向传播、影响内存使用和性能

【Paddle】Inplace相关问题&#xff1a;反向传播、影响内存使用和性能 写在最前面inplace 的好处有哪些&#xff1f;能降低计算复杂度吗在反向传播时&#xff0c;Inplace为什么会阻碍呢&#xff1f;“计算图的完整性受损”表达有误原地操作 sin_()为什么原地操作会阻碍反向传播…