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

news2024/12/29 10:07:55

文章目录

  • 1、合并区间
  • 2、无重叠区间
  • 3、用最少的箭引爆气球
  • 4、整数替换
  • 5、俄罗斯套娃信封
  • 6、可被3整除的最大和
  • 7、距离相等的条形码
  • 8、重构字符串


1、合并区间

56. 合并区间

在这里插入图片描述
在合并区间时,得先排序一下,方便判断。方便可以按照左或者右端点排序。很多问题左右端点都可以排序,这里就用左端点排序。

用左端点排序,排好后的所有区间,可以发现能够合并的区间都会是连续的。合并本质上就是在求并集,有可能连续几个区间左端点数字都相同,但右端点不同,这部分就得用最大的右端点做新的右端点;左端点的不同,有3种情况:右端点小于,大于等于上一个右端点,以及左端点大于上一个右端点。

假设第一个区间左右端点是left和right,第二个区间左右端点是ab。如果两个区间有重叠部分(即使只有一个端点也是重叠),那么a <= right,新区间就是[left, max(right, b)];如果a > right,那就不重叠,不合并,更新left和right。

    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());
        int left = intervals[0][0], right = intervals[0][1];
        vector<vector<int>> res;
        for(int i = 1; i < intervals.size(); ++i)
        {
            int a = intervals[i][0], b = intervals[i][1];
            if(a <= right) right = max(right, b);
            else
            {
                res.push_back({left, right});
                left = a;
                right = b;
            }
        }
        //最后一个区间其实没加入
        res.push_back({left, right});
        return res;
    }

2、无重叠区间

435. 无重叠区间

在这里插入图片描述
先看上一题。和上一题不同的是,这道题需要保留尽可能多的不重叠的区间,也就是移除最少的区间。

关于区间重叠的所有情况,画图就能很好地展现出来,这个也简单,就不放图了。对于左端点相同,右端点不同的,应当留小的那个区间,因为更长的区间就更有可能和其它区间相交,这种情况就是right > b,留b所在的区间。其它情况也是如此,我们要留下更小的区间。

a < right,留下min(right, b),等于的情况不需要考虑;a >= right,也就是出现了不相交的情况,那就用下一个区间的右端点继续去判断,也就是更新right为b。

    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());
        int left = intervals[0][0], right = intervals[0][1];
        int res = 0;
        for(int i = 1; i < intervals.size(); ++i)
        {
            int a = intervals[i][0], b = intervals[i][1];
            if(a < right)
            {
                res++;
                right = min(right, b);
            }
            else right = b;
        }
        return res;
    }

3、用最少的箭引爆气球

452. 用最少数量的箭引爆气球

在这里插入图片描述
在这里插入图片描述
先看合并区间和重叠区间这两道题。一支箭希望尽量引爆更多气球。根据之前两个题目,按照左端点排序后,如果求交集,会发现互相重叠的区间是连续的,如果求并集,会发现能够合并的区间是连续的。交集出现的这个性质其实是比较强的,三个区间可以合并,但有可能第三个区间和第一个区间无法合并,它们需要通过第二个区间才能三个全部合并,而重叠就无法这样。

对于这道题来说,我们要找的应当是重叠的区间,也就是右端点小于下一个左端点。我们可以求出两个区间的交集,让这个交集去和第三个区间比较,只要都能重叠,那就可以继续求交集,比较下去,交集会逐渐变小。

当a <= right时,交集区间是[max(left, a), min(right, b)];当遇到不重叠的时候,就得另发一支箭了。

    int findMinArrowShots(vector<vector<int>>& points) {
        sort(points.begin(), points.end());
        int right = points[0][1];
        int res = 1;
        for(int i = 1; i < points.size(); ++i)
        {
            int a = points[i][0], b = points[i][1];
            if(a <= right) right = min(right, b);
            else
            {
                res++;
                right = b;
            }
        }
        return res;
    }

4、整数替换

397. 整数替换

在这里插入图片描述
先来看一些二进制知识。

如果是偶整数,二进制表示中最后一位是0;如果是奇整数,二进制表示中最后一位是1;除2操作就是二进制表示中统一右移一位。

在这个题目中,如果是偶数,那就只能除2;如果是奇数,那就有两个选择,+1或-1,我们得判断走哪步更好。

二进制位的最后两位,可能是01,可能是11。对于01,+1变成10,-1变成00,这时候都可以除2,假设原二进制位前面几个是0,那么这时候10和00都除2的话,就变成了01和00,这时候明显00更好,它可以除2,而01只能继续做+1或者-1,所以对于01这个的二进制末二位,-1再除2更好。

对于…11,+1变成…00,-1变成10,这时候也都可以除2,但实际上00除2更好,这个情况可以自己列举。而如果数字是3,二进制表示就是00000011,那么-1再除2就好了。

对于01或11的判断,用数字 % 4就好。

    int integerReplacement(int n) {
        int res = 0;
        while(n > 1)
        {
            if(n % 2 == 0)
            {
                n /= 2;
                res++;
            }
            else
            {
                if(n == 3)
                {
                    res += 2;
                    n = 1;
                }
                else if(n % 4 == 1)
                {
                    res+= 2;
                    n /= 2;
                }
                else
                {
                    n = n / 2 + 1;
                    res += 2;
                }
            }
        }
        return res;
    }

5、俄罗斯套娃信封

354. 俄罗斯套娃信封问题

在这里插入图片描述
这道题最终会变成最长递增子序列的问题,用到这个问题的动规、贪心 + 二分思路,所以事先知道会更好,这道题也会在此基础上来写。我的博客动规5,贪心2中有最长递增子序列。

常规解法,虽然这道题会超时,但思路有用。和上面几个题一样,先按照左端点排序就不用去比较右边的区间了,只需要去左边比较右端点了。当排完序后,这其实就像一个最长递增子序列,只要后面的能套前面的,就可以连在一起,就是一个序列的。用动规思路。

状态表示:dp[i]是以i位置的信封为结尾的所有的套娃序列中,最长的序列的长度。

确定状态转移方程:0~i - 1的所有位置中,找到某个区间j,只要i能套j,那么就是dp[j] + 1。所以方程就是max(dp[j] + 1),需要的最大长度,条件则是0 <= j < i,e[i][0] > e[j][0],e[i][1] > e[j][1]。

初始化:dp表全初始化为1,因为1个区间可以套娃自己。

返回值:dp表里最大值。

    int maxEnvelopes(vector<vector<int>>& e) {
        //动规
        sort(e.begin(), e.end());
        int n = e.size();
        vector<int> dp(n, 1);
        int res = 1;
        for(int i = 1; i < n; ++i)
        {
            for(int j = 0; j < i; ++j)
            {
                if(e[i][0] > e[j][0] && e[i][1] > e[j][1])
                    dp[i] = max(dp[j] + 1, dp[i]);
            }
            res = max(res, dp[i]);
        }
        return res;
    }

适用的解法是重写排序 + 贪心。因为只是左端点排序后就用贪心,会需要考虑很多细节。

左端点排完序后,可能会出现几个区间左端点都相同,而右端点不同,这时候要考虑的就是右端点的顺序,如果右端点此时从大到小排序,就可以尽量地套娃下去;当左端点不同的时候,左端点从小到大排序,就不需要再排右端点。

    int maxEnvelopes(vector<vector<int>>& e) 
    {
        //贪心
        sort(e.begin(), e.end(), [&](const vector<int>& v1, const vector<int>& v2)
        {
            return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] > v2[1];
        });
        vector<int> res;
        res.push_back(e[0][1]);
        for(int i = 1; i < e.size(); ++i)
        {
            int b = e[i][1];
            if(b > res.back())
            {
                res.push_back(b);
            }
            else
            {
                int left = 0, right = res.size() - 1;
                while(left < right)
                {
                    int mid = (left + right) / 2;
                    if(res[mid] >= b) right = mid;
                    else left = mid + 1;
                }
                res[left] = b;
            }
        }
        return res.size();
    }

6、可被3整除的最大和

1262. 可被三整除的最大和

在这里插入图片描述
既然要得到最大的数,不妨在最一开始把所有数都加起来,模3如果等于0的话就直接返回这个数。

模3的结果还有2种情况,也就是余数1和2的情况,假设x是模3余数为1的数,y是余数为2的数。如果出现了% 3 == 1的情况,那么去掉这个x就没问题了,此时要么剩余的数的和是3的倍数,要么中间有几个有余数的数,但它们加起来就是3的倍数了,最终抵消了。所有整体可以看作去掉余数为1的这个x,其余数的和就是3的倍数。这种情况,要删的就是余数为1的这个数。

除此之外,余数为1的情况,不只是有一个x,可能有两个y,也就是两个余数为2的,加起来就是余数为4,那么% 3的话也是1,也还有更多的y,但和上面一样,最终都是2个y的情况,那么为了消除这个余数为1的情况,把最小的和次小的y去除就好。

余数为1的情况,综上所述,取两个情况的最大值,max(sum - x1, sum - y1 - y2)。

余数为2的情况。要么是一个y,要么是2个x。根据上面的思路,要最小的y,或最小和次小的两个x,max(sum - y1, sum - x1 - x2)。

求一堆数的最小值和次小值,可以用sort,这样最快是nlogn的时间复杂度,可以O(n)来解决,用分类讨论的方法。先选定两个数,每当新来一个数,就去和这两个数比较,一次次更新,最终得到的就是最小和次小的。如果x < x1,则x2 = x1, x1 = x;如果x1 <= x <= x2,则x2 = x。

    int maxSumDivThree(vector<int>& nums) {
        const int INF = 0x3f3f3f3f;
        int sum = 0, x1 = INF, x2 = INF, y1 = INF, y2 = INF;
        for(auto x : nums)
        {
            sum += x;
            if(x % 3 == 1)
            {
                if(x < x1)
                {
                    x2 = x1;
                    x1 = x;
                }
                else if(x < x2) x2 = x;
            }
            else if(x % 3 == 2)
            {
                if(x < y1)
                {
                    y2 = y1;
                    y1 = x;
                }
                else if(x < y2) y2 = x;
            }
        }
        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);
    }

7、距离相等的条形码

1054. 距离相等的条形码

在这里插入图片描述
把所有相同的数看成一个块,一个数组有好几个块,每个块的数字都隔一个位置来放置,这样就可以有效防止出现相邻数字相同。但这样还有问题,如果是122这样的数字,1放在第一个位置,后面没有1,再看2,两个2会放在相邻位置,所以要先处理出现次数最多的数,这样结果就是212,2先隔一个位置放置,再放置1。剩下的数的处理顺序无所谓,都按照隔一个位置放置的规则就行,每次只处理一批相同的数字。

    vector<int> rearrangeBarcodes(vector<int>& b) {
        unordered_map<int, int> hash;//统计每个数出现的频次
        int maxVal = 0, maxCount = 0;//出现次数最多的数和最多的次数
        for(auto x : b)
        {
            if(maxCount < ++hash[x])//统计一下频次再和maxCount比较
            {
                maxCount = hash[x];
                maxVal = x;
            }
        }
        int n = b.size();
        vector<int> res(n); 
        int index = 0;
        for(int i = 0; i < maxCount; i++)
        {
            res[index] = maxVal;
            index += 2;
        }
        hash.erase(maxVal);
        for(auto& [x, y] : hash)//这样是把x作为哈希表的key,y作为哈希表的value
        {
            for(int i = 0; i < y; i++)
            {
                if(index >= n) index = 1;
                res[index] = x;
                index += 2;
            }
        }
        return res;
    }

8、重构字符串

767. 重构字符串

在这里插入图片描述
和条形码基本一样的思路。在处理之前,如果次数最多的那个 > (n + 1) / 2,那么一定无法重排,就像例2。

    string reorganizeString(string s)
    {
        int hash[26] = {0};
        char maxChar = ' ';
        int maxCount = 0;
        for(auto ch : s)
        {
            if(maxCount < ++hash[ch - 'a'])
            {
                maxChar = ch;
                maxCount = hash[ch - 'a'];
            }
        }
        int n = s.size();
        if(maxCount > (n + 1) / 2) return "";
        string res(n, ' ');
        int index = 0;
        for(int i = 0; i < maxCount; i++)
        {
            res[index] = maxChar;
            index += 2;
        }
        hash[maxChar - 'a'] = 0;
        for(int i = 0; i < 26; i++)
        {
            for(int j = 0; j < hash[i]; j++)
            {
                if(index >= n) index = 1;
                res[index] = 'a' + i;
                index += 2;
            }
        }
        return res;
    }

结束。

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

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

相关文章

StyleGAN 使用指南:生成更逼真的图片

StyleGAN 使用指南&#xff1a;生成更逼真的图片 提出背景&#xff1a;特征纠缠StyleGAN-v1 网络结构映射网络 Mapping network f生成网络 Synthesis network g训练技巧样式混合 mixing regularization截断 Truncation Trick 评估指标路径长度 Perceptual path length解耦&…

2019年3月14日 Go生态洞察:Go开发者网络新动态

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

洗眼镜的机器有哪些?眼镜超声波清洗机哪家强?眼镜清洗机推荐

由于自己经常佩戴眼镜&#xff0c;所以个人对清洗眼镜的要求害死挺高的&#xff0c;一周最少是需要清洗一次的&#xff0c;不然会感觉眼镜上会有非常多的细菌藏匿在污垢中&#xff0c;往往这些细菌没有清洁到位就会导致脸上长痘长螨虫的问题&#xff0c;所以看到这里的朋友们一…

灵茶 - 2023 - 11 - 27

B. Minimum Ternary String 链接 : Problem - 1009B - Codeforces 思路 : 10 , 12 可以互相交换&#xff0c;就代表着1可以出现在任何地方&#xff0c;要追求字典序最小&#xff0c;那么应该将所有的1放在哪里呢 ? 应该放在第一个2前面&#xff0c;要注意的是 : 没有2的…

C++基础 -3- 匿名空间,命名空间跨文件使用

匿名空间仅限本文件使用 创建匿名空间格式(图片代码段呈现) namespace {int a100; }命名空间的跨文件使用 namespace rlxy {int a100; }namespace rlxy {extern int a; }

深度学习之循环神经网络

视频链接&#xff1a;6 循环神经网络_哔哩哔哩_bilibili 给神经网络增加记忆能力 对全连接层而言&#xff0c;输入输出的维数固定&#xff0c;因此无法处理序列信息 对卷积层而言&#xff0c;因为卷积核的参数是共享的&#xff0c;所以卷积操作与序列的长度无关。但是因为卷积…

可以媲美MidJourney,但是开源和免费的超强AI绘画-fooocus

现在的AI技术很火&#xff0c;这一新兴领域的发展给艺术创作带来了全新的可能性&#xff0c;AI绘画的应用也十分的广泛。我自己见到的就有运用在模特穿衣服身上的&#xff0c;化妆品广告图生成的等等&#xff0c;比如MidJourney 和stable diffusion 等 AI绘画程序已经很成熟了&…

011 OpenCV warpAffine

目录 一、环境 二、warpAffine原理 三、完整代码 一、环境 本文使用环境为&#xff1a; Windows10Python 3.9.17opencv-python 4.8.0.74 二、warpAffine原理 warpAffine是OpenCV库中的一个函数&#xff0c;它用于执行二维仿射变换。这个函数接受一个输入图像和变换矩阵&…

【Linux 静态IP配置】

静态IP配置 1.NAT模式设置2.设置静态ip3.重启网络4.查看ip 1.NAT模式设置 首先设置虚拟机中NAT模式的选项&#xff0c;打开VMware&#xff0c;点击“编辑”下的“虚拟网络编辑器”&#xff0c;设置NAT参数 注意&#xff1a; VMware Network Adapter VMnet8保证是启用状态 …

针对近期大面积出现的小程序新用户登录不正常处理办法

原因是微信小程序需要更新协议规则 操作方法如下&#xff1a; 提交之后等待通过

麒麟信安联合牵头 | 国家关键领域信创行业产教融合共同体成立大会暨欧拉人才发展论坛盛大召开

11月24日&#xff0c;国家关键领域信创行业产教融合共同体成立大会暨欧拉人才发展论坛在长沙职业技术学院盛大召开。大会由湖南省教育厅、湖南省工业和信息化厅、长沙市人民政府指导&#xff0c;麒麟信安、长沙理工大学、长沙职业技术学院三家牵头单位主办&#xff0c;湖南欧拉…

01-Java集合之单向队列,如Collection接口,List接口,Set接口,Queue接口及其实现类的底层结构和特点

单列集合 特点 单列集合分为三大类 List类型的集合: 有序可重复 , 这种类型的集合的元素都有下标Set类型的集合: 无序不可重复 , 这种类型的集合的元素都没有下标Queue类型的集合: 先进先出(FIFO) , 只能一端进并且在另一端出的队列 Collection中能存放的元素: 没有使用泛型…

国家万亿资金助力城市生命线城市内涝积水监测系统

自2023年年初以来&#xff0c;我国多个地区遭遇了洪涝、干旱、台风、风雹等灾害的侵袭&#xff0c;部分地区灾情严重&#xff0c;经济损失较大。为应对灾后恢复重建工作的艰巨任务&#xff0c;本次国债将主要投向灾后恢复重建以及提升防灾减灾救灾能力。其中&#xff0c;将全面…

统信UOS安装Virtualbox虚拟机和Windows10系统

在UOS统信系统中部署Windows环境我可以通过安装虚拟机来实现&#xff0c;这也可以解决软件不适配带来的一些问题&#xff0c;当然对硬件配置也有一定的要求&#xff0c;不建议性能过低的设备使用。 接下来请按照以下步骤进行安装Virtualbox及Win10虚拟系统的设置。 1、安装Vi…

docker容器的生命周期管理常用命令

容器的生命周期管理命令 docker create &#xff1a;创建一个新的容器但不启动它 docker create nginx docker run :创建一个新的容器并运行一个命令 常用选项&#xff1a; 常用选项1. --add-host&#xff1a;容器中hosts文件添加 host:ip 映射记录 2. -a, --attach&#…

【论文阅读】ActiveNeRF:通过不确定性估计候选新视图

【论文阅读】ActiveNeRF: Learning where to See with Uncertainty Estimation Abstract1 Introduction3 Background4 NeRF with Uncertainty Estimation5 ActiveNeRF5.1 Prior and Posterior Distribution5.2 Acquisition Function5.3 Optimization and Inference 6 Experimen…

Redis面试题:Redis是单线程的,但是为什么还那么快?I/O多路复用模型

目录 面试官&#xff1a;Redis是单线程的&#xff0c;但是为什么还那么快&#xff1f; 面试官&#xff1a;能解释一下I/O多路复用模型&#xff1f; 面试官&#xff1a;Redis是单线程的&#xff0c;但是为什么还那么快&#xff1f; 候选人&#xff1a; 嗯&#xff0c;这个有几…

Robot Framework自动化测试(四)--- 分层思想

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

【广州华锐互动】节约用水VR互动教育:身临其境体验水资源的珍贵!

随着技术的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术在许多领域得到了广泛应用。在节水宣传教育方面&#xff0c;VR技术也展现出了其独特的优势。与传统宣传教育方式相比&#xff0c;节约用水VR互动教育具有更加沉浸式、互动性和实践性的特点&#xff0c;能…

Java计算二叉树从根节点到叶子结点的最大路径和

要求从根节点到叶子结点的最大路径和&#xff0c;可以通过递归遍历二叉树来实现。对于二叉树中的每个节点&#xff0c;我们都可以考虑包含该节点的最大路径和。在递归的过程中&#xff0c;我们需要不断更新全局最大路径和。 具体的思路如下&#xff1a; 递归函数设计&#xff1…