【贪心算法】区间类算法题(整数替换、俄罗斯套娃、重构字符串等、C++)

news2024/11/25 23:27:23

文章目录

  • 1. 前言
  • 2. 算法题
    • 1.整数替换
    • 2.俄罗斯套娃信封问题
    • 3.可被三整除的最大和
    • 4.距离相等的条形码
    • 5.重构字符串

1. 前言

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

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

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

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

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


2. 算法题

1.整数替换

在这里插入图片描述

思路

题目求解将整数 n 转换为 1 的最小操作次数。操作可以是将 n 变为 n/2(当 n 是偶数时)或将 n 增加或减少 1(当 n 是奇数时):

  1. 如果 n 是偶数:直接将 n 除以 2,并增加操作计数 ret
  2. 如果 n 是奇数
    • 特例:当 n 是 3 时,直接将其变为 1,共需要两次操作。
    • 一般情况:根据 n 的末尾二进制位判断操作:
      • 末尾为 01n % 4 == 1,则将 n 减 1 并除以 2。
      • 末尾为 11n % 4 == 3,则将 n 加 1 并除以 2。

代码

class Solution {
public:
    int integerReplacement(int n) {
        // 贪心
        int ret = 0;
        while(n > 1)
        {
            if(n % 2 == 0)
            {
                n /= 2;
                ++ret;
            }
            else
            {
                // 以二进制进行分析
                if(n == 3){
                    ret += 2;
                    n = 1;
                } else if (n % 4 == 1) { // ....01
                    n = (n - 1) / 2;
                    ret += 2;
                } else { // ...11
                    n = n / 2 + 1;
                    ret += 2;
                }
            }    
        }
        return ret;
    }
};

2.俄罗斯套娃信封问题

在这里插入图片描述

思路

题目要求解“俄罗斯套娃信封”问题,即找出能在彼此中嵌套的最大信封数目。核心思路是:贪心 + 二分

  1. 排序:首先对信封按左端点升序排序,如果左端点相同,则按右端点降序排序。这确保了左端点相同的信封不会嵌套。

  2. 贪心 + 二分搜索

    • 使用一个动态数组 ret 来存储当前有效的信封序列的右端点。
    • 遍历每个信封的右端点 b
      • 如果 b 大于 ret 的最后一个元素,表示可以将 b 追加到 ret 的末尾。
      • 否则,使用二分搜索在 ret 中找到第一个不小于 b 的位置,并替换该位置的值,以维持 ret 的非递减顺序。
  3. 返回结果ret 的大小即为最大嵌套信封的数目。

这个算法的时间复杂度为 O(n log n),其中 n 是信封的数量。

class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), [&](const vector<int>& v1, const vector<int>& v2){
            // 左端点不同,则按左端点排序; 左端点相同,则按右端点排序降序
            return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] > v2[1];
        });

        // 贪心 + 二分优化
        vector<int> ret;
        ret.push_back(envelopes[0][1]);
        for(int i = 1; i < envelopes.size(); ++i)
        {
            int b = envelopes[i][1];
            if(b > ret.back())
            {
                ret.push_back(b);
            }
            else
            {
                // 二分: 到第一个大于或等于当前元素的位置
                int left = 0, right = ret.size() - 1;
                while(left < right)
                {
                    int mid = (left + right) >> 1;
                    if(ret[mid] >= b) right = mid;
                    else left = mid + 1;
                }
                ret[left] = b;
            }
        }

        return ret.size();
    }
};

3.可被三整除的最大和

在这里插入图片描述

思路

题目要求计算一个数组 nums 中的最大和,使其能被3整除。具体步骤如下:

  1. 初始化变量sum 记录数组总和,x1, x2 分别记录 %3 == 1 的最小和次小数,y1, y2 记录 %3 == 2 的最小和次小数。

  2. 遍历数组:累加 sum,同时更新 x1, x2, y1, y2 记录对应的最小值。

  3. 分类讨论

    • 如果 sum % 3 == 0,则 sum 已经可以被3整除,直接返回。
    • 如果 sum % 3 == 1,考虑去掉 %3 == 1 的最小值或去掉 %3 == 2 的最小两个值(以保证总和能被3整除),取两者的最大值。
    • 如果 sum % 3 == 2,类似地,考虑去掉 %3 == 2 的最小值或去掉 %3 == 1 的最小两个值,取两者的最大值。
  4. 返回结果:如果没有满足条件的结果,返回 -1

代码

class Solution {
public:
    int maxSumDivThree(vector<int>& nums) {
        const int INF = 0x3f3f3f3f;

        // x1,x2: 标记%3==1的数 的最小与次小数
        // y1,y2: 标记%3==2的数 的最小与次小数
        int sum = 0, x1 = INF, x2 = INF, y1 = INF, y2 = INF;
        for(auto num : nums)
        {
            sum += num;
            if(num % 3 == 1)
            {
                if(num < x1) x2 = x1, x1 = num;
                else if(num < x2) x2 = num;
            }
            else if (num % 3 == 2)
            {
                if(num < y1) y2 = y1, y1 = num;
                else if(num < y2) y2 = num;
            }
        }

        // 分类讨论
        if(sum % 3 == 0) return sum;
        else if (sum % 3 == 1) return max(sum - x1, sum - y1 - y2);
        // else return max(sum - y1, sum - x1 - x2); 
        else if (sum % 3 == 2) return max(sum - y1, sum - x1 - x2); 

        return -1;
    }
};

4.距离相等的条形码

在这里插入图片描述

思路

题目要求重新排列 barcodes 中的元素,使得相同元素不相邻。下面是代码的步骤:

  1. 统计元素频率:使用 unordered_map 记录每个元素的出现次数,并找到出现次数最多的元素 maxVal 和其出现次数 maxCount

  2. 插入最多的元素:在结果向量 ret 的偶数位置(0, 2, 4, ...)中填充最多出现的元素 maxVal

  3. 插入其余元素:将其他元素插入到结果向量中,首先填充偶数位置后,如果偶数位置已满,则从奇数位置开始填充。

  4. 返回结果向量:生成的向量符合要求,即相同元素不相邻。

代码

class Solution {
public:
    vector<int> rearrangeBarcodes(vector<int>& barcodes) {
        unordered_map<int, int> hash; // 用于记录出现次数最多的那个数
        int maxVal = 0, maxCount = 0;
        for(auto x : barcodes)
        {
            if(maxCount < ++hash[x])
                maxVal = x, maxCount = hash[x];
        }

        // 插入出现次数最多的那个元素maxVal
        vector<int> ret(barcodes.size());
        int index = 0;
        for(int i = 0; i < maxCount; ++i)
        {
            ret[index] = maxVal;
            index += 2;
        }

        // 插入其余元素
        hash.erase(maxVal);
        for(auto [a, b] : hash)
        {
            // 根据每一位的次数插入
            for(int i = 0; i < b; ++i)
            {
                // 超出数组范围后从奇数开始插入元素
                if(index >= barcodes.size()) index = 1;
                ret[index] = a;
                index += 2;
            }
        }
        return ret;
    }
};

5.重构字符串

在这里插入图片描述

思路

题目要求重排字符串 s,使得相同字符不相邻。代码步骤如下:

  1. 统计字符频率:使用 unordered_map 记录每个字符的出现次数,并找出出现次数最多的字符 maxCh 和其出现次数 maxCount

  2. 检查可重排性:如果 maxCount 超过 (n + 1) / 2,则无法重排,直接返回空字符串。

  3. 填充结果字符串

    • 处理最多的字符:首先将出现次数最多的字符 maxCh 插入到结果字符串的所有偶数位置(0, 2, 4, ...)。
    • 处理其余字符:将其余字符插入到结果字符串的空闲位置中,先填充偶数位置,如果偶数位置填满,则转到奇数位置。
  4. 返回结果字符串:生成的字符串符合要求,确保相同字符不相邻。

代码

class Solution {
public:
    string reorganizeString(string s) {
        unordered_map<char, int> hash; // 统计字符以及出现次数
        // 找出现次数最多的字符
        char maxCh = ' '; 
        int maxCount = 0, n = s.size();
        for(auto ch : s)
        {
            if(maxCount > (n + 1) / 2) return ""; // 不能重构
            if(maxCount < ++hash[ch])
            {
                maxCount = hash[ch];
                maxCh = ch;
            }
        }

        // 处理出现次数最多的字符
        int index = 0;
        string ret(n, ' '); // 结果字符串
        for(int i = 0; i < maxCount; ++i)
        {
            // ret.insert(index, 1, maxCh);
            ret[index] = maxCh;
            index += 2;
        }

        // 处理其余字符
        hash.erase(maxCh);
        for(auto [a, b] : hash)
        {
            // 对每个元素进行次数插入
            for(int j = 0; j < b; ++j)
            {
                if(index >= n) index = 1;
                ret[index] = a;
                index += 2;
            }
        }

        return ret;
    }
};

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

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

相关文章

sqlite数据插入效率

一、程序效率测试 时间相关接口&#xff1a; int gettimeofday(struct timeval*tv, struct timezone *tz); 功能&#xff1a;得到从1970年1月1日0时0分0秒到现在的秒数。<可以利用该函数来计算一个程序的运行时间&#xff0c;只需在程序前后调用该函数&#xff0c;…

安捷伦Agilent N9344C手持频谱分析仪

安捷伦Agilent N9344C手持频谱分析仪 安捷伦Agilent N9344C手持频谱分析仪&#xff08;HSA&#xff09; 主要特性和功能 获得专为现场使用设计的性能&#xff1a;便携耐用&#xff0c;无风扇设计&#xff1b;无论白天还是夜晚均可显示清晰的画面&#xff1b;电池使用时间为 4 …

大模型开发转行全攻略:必备知识、技能与学习路径详解,大模型零基础入门到精通

引言 随着人工智能和大模型&#xff08;如GPT-4、BERT等&#xff09;技术的快速发展&#xff0c;越来越多的专业人士希望转行进入这一领域。大模型开发涉及复杂的技术体系和多样的应用场景&#xff0c;对从业者的知识和能力提出了较高要求。本文将详细解析转行大模型开发所需的…

CasaOS系统本地安装Gopeed高速下载软件并实现异地远程访问下载文件

文章目录 前言1. 更新应用中心2.Gopeed安装与配置3. 本地下载测试4. 安装内网穿透工具5. 配置公网地址6. 配置固定公网地址 前言 本文主要介绍如何在轻NAS系统CasaOS小主机中安装支持全平台的高速下载器Gopeed&#xff0c;并结合内网穿透工具配置公网地址实现远程访问本地主机…

java知识点——I/O输入输出处理(二)

读取字符流&#xff1a; 由于字符输入流的read()方法返回的是int类型的值&#xff0c;如果想获得字符就需要进行强制类型转换&#xff0c;所以输出语句中将变量len强转为了char类型。 public static void main(String[] args) throws IOException {//创建FileReader对象FileRe…

PHP一体化解决方案高效整合与优化学校管理系统小程序源码

一体化解决方案&#xff0c;让学校管理系统焕发新生✨ &#x1f3eb; 开篇&#xff1a;传统管理的瓶颈与挑战 在快节奏的教育时代&#xff0c;传统的学校管理系统是否让你感到力不从心&#xff1f;&#x1f914; 信息孤岛、流程繁琐、效率低下...这些问题是否正困扰着你的工作…

巴黎奥运会引发体育健身热潮:气膜体育馆成为新宠—轻空间

随着巴黎奥运会的成功举办&#xff0c;全球范围内掀起了一股体育健身的热潮。各地的健身场所迎来了前所未有的参与热情&#xff0c;其中&#xff0c;融合了体育、娱乐、休闲等多种业态的综合气膜体育馆因其独特的优势&#xff0c;迅速成为群众健身的新宠&#xff0c;成为了大众…

PHP一键创建全球参与探索现代在线投票系统

一键创建全球参与探索现代在线投票系统 &#x1f310;✨ &#x1f680; 开篇&#xff1a;解锁全球互动新纪元 在这个数字化飞速发展的时代&#xff0c;每一个声音都值得被听见&#xff0c;每一份意见都能跨越山海相连。想象一下&#xff0c;只需轻轻一点&#xff0c;就能发起…

如何读懂以太坊源代码

以下是一个学习以太坊源代码的清晰思路&#xff1a; 一、前期准备 基础知识储备&#xff1a; 熟悉区块链的基本概念&#xff0c;如区块、交易、共识机制、哈希函数等。了解 Go 语言&#xff08;以太坊主要使用 Go 语言编写&#xff09;的基本语法和编程概念。 工具准备&#x…

包装和类练习(1)

目录 1.移除元素 2.删除有序数组中的重复项 3.合并两个有序数组 4.实现ArrayList类 4.下列在Java语言中关于数据类型和包装类的说法&#xff0c;正确的是&#xff08;&#xff09; 5. boolean类型没有包装用的类&#xff0c;这句话是否正确&#xff1f; 1.移除元素 这里使用…

今年读过最绝的一本书!几乎把ChatGPT讲透了——<ChatGPT 从⼊⻔到精通>

这本书从⼊⻔到精通&#xff0c;介绍了 ChatGPT 的基础知识、实现⽅法、进阶应⽤以及最新进展等多个⽅⾯。 ChatGPT 作为⼀种强⼤的⾃然语⾔处理模型&#xff0c;其应⽤前景⼴泛&#xff0c;可以应⽤于⾃然语⾔⽣成、对话系统、推荐系统、知识图谱构建、智能家居、⼈⼯智能教育…

【SQL】常见语句合集

SQL常见语句合集 一. 新建表1.1 语句1.2 结果 二. 新增数据2.1 语句2.2 结果 三. 新增字段列3.1 语句3.2 结果3.3 扩展 四. 更新指定数据4.1 语句4.2 结果 五. 更新指定列5.1 语句&#xff08;长度&#xff09; 六. 删除字段列6.1 语句 七. 删除指定数据7.1 语句 八. 查询 一. …

Spring拦截器与Servlet过滤器区别

前言 温馨提醒:希望读者了解Spring拦截器和Servlet过滤器基础知识 目录 前言 位置和层面 使用场景 配置方式 生命周期 执行顺序 总结 Spring拦截器基础知识https://blog.csdn.net/Dreamkidya/article/details/141825688?spm1001.2014.3001.5501 Servlet过滤器基础知识h…

计算机视觉软件教学平台

1、基本介绍 计算机视觉软件教学平台是中智讯公司开发的一款面向人工智能相关专业机器视觉方向的综合型实验平台&#xff0c;主要满足&#xff1a;图像处理、图像识别、机器视觉应用、边缘计算应用、智能算法等课程的实验和实训&#xff0c;是基于新工科和工程教育思维和专业改…

IPv6的部署会影响现有IPv4网络的运行吗

IPv6是新一代的互联网协议&#xff0c;到现在为止&#xff0c;担心它是否会影响现有IPv4网络的运行的人不在少数。 先来了解下IPv4和IPv6的区别。IPv4使用的是32位地址&#xff0c;IPv6使用的是128位地址&#xff0c;很多人现在不理解了&#xff0c;这和地址数有关系吗&#xf…

【从零开始学爬虫】采集58同城房源数据

本文以采集北京市58同城房源数据为例进行演示&#xff1a; l 采集网站 【场景描述】采集58同城房源数据。 【使用工具】前嗅ForeSpider数据采集系统 http://www.forenose.com/view/commodity/forespider.html 【入口网址】 https://bj.58.com/xiaoqu/?PGTID0d000000-000…

【小呆的热力学笔记】理想热力循环

文章目录 5.理想热力循环5.1 卡诺定理5.2 卡诺循环5.3 逆向卡诺循环 5.理想热力循环 5.1 卡诺定理 考虑这样的一个实际的热力循环&#xff0c;其过程为存在一个高温热源&#xff0c;工作介质从该高温热源中吸取热量Q1&#xff0c;其中一部分通过工作介质对外做功转化为机械功…

idea安装并使用maven依赖分析插件:Maven Helper

在 IntelliJ IDEA 中安装并使用 Maven Helper 插件可以帮助你更方便地管理 Maven 项目的依赖&#xff0c;比如查看依赖树、排除冲突依赖等。以下是安装和使用 Maven Helper 插件的步骤&#xff1a; 安装 Maven Helper 插件 打开 IntelliJ IDEA 并进入你的项目。 在 IDE 的右下…

【408 数据结构】第1章绪论

文章目录 绪论考纲DS 基本概念1. 基本概念2. 数据结构三要素 算法&#xff08;时/空间复杂度计算&#xff09;1. 算法概念2. 算法效率的度量时间复杂度&#xff1a;空间复杂度&#xff1a; 小结 绪论 考纲 计算时间复杂度和空间复杂度&#xff08;重点难点&#xff09; DS …

简化物业数据管理:使用 Indexify 进行高级数据提取和检索

使用 Indexify 进行文档查询的分步指南。欢迎来到雲闪世界。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 总结&#xff1a; 传统的数据提取方法通常会错过非结构化内容的更深层次的见解&#xff0c;尤其是在房地产领域。 本文探讨使用 Indexify&…