当前位置: 首页 > news >正文

贪心系列专题篇四

目录

整数替换

解法一

解法二

俄罗斯套娃信封问题

堆箱子

可被三整除的最大和

距离相等的条形码

重构字符串


声明:接下来主要使用贪心法来解决问题!!!

整数替换

题目

思路

下面将使用两种方法来解决这道题,第一种方法是递归+记忆化搜索;第二种方法是贪心。

解法一

使用递归+记忆化搜素来解决,因为递归当对数进行两种操作时,会用到相同的值,因此记录下每个数的最小替换次数将会更佳。

代码

class Solution {
public:int integerReplacement(int n) {return dfs(n);}int dfs(long long n){if(n==1)return 0;if(n%2==0)return 1+dfs(n/2);elsereturn 1+min(dfs(n+1),dfs(n-1));}
};//只递归版class Solution {unordered_map<int,int> hash;
public:int integerReplacement(int n) {return dfs(n);}int dfs(long long n){if(hash.count(n))return hash[n];if(n==1){hash[1]=0;return hash[1];}if(n%2==0){hash[n]=1+dfs(n/2);return hash[n];}else{hash[n]=1+min(dfs(n+1),dfs(n-1));return hash[n];}}
};//递归+记忆化搜素
解法二

依题意,对于偶数,只执行除2操作,对奇数有+1和-1操作,那么如何能区分出+1和-1哪个操作更优那?

将奇数分成3类进行讨论:分别是3,大于3且模4等于1,大于3且模4等于3.

贪心策略

对于3,最少操作次数确定为2;

对于大于3且模4等于1,易知进行-1操作优于+1操作;

对于大于3且模4等于3,易知进行+1操作优于-1操作;

代码

class Solution {
public:int integerReplacement(long long 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){n-=1;ret++;}else if(n%4==3){n+=1;ret++;}}}return ret;}
};
俄罗斯套娃信封问题

题目

思路

首先我们先对集合按区间左端点进行排序,因为如果不排序的话,比较两个区间左端点的大小是需要不断遍历集合的。

通过分析这道题,我们会发现就是要找出一个最长的俄罗斯套娃序列即可,与《最长递增子序列》几乎是如出一辙,可以使用动态规划和定义排序+贪心+二分来解决,但是使用动态规划的时间复杂度是O(N^2),对于本道题是会超时的,下面将使用定义排序+贪心+二分来解决,时间复杂度是O(N^logN)的,贪心+二分是怎样的,如下:
我们知道,对于一个递增子序列,如果递增子序列某个位置的数在原始数组中该数后面的数比它小,那么可以以这个较小的数替换它,显而易见是可以的,不过这样找出来的结果虽然不是对应的最长递增子序列所对应的数,但是长度是一致的。

更新规则如下:从前往后扫描数组,当找到的数大于已记录的数,就把这个数放到长度更长的对应位置;如果找到的数大于某个位置的数,就往后找到合适的位置;当找到的数小于等于某个位置的数,就覆盖这个位置的数。

优化:扫描整个数组是必不可少的,可优化的地方就是找对应合适的位置,可使用二分查找代替再次遍历整个数组。

但是为什么要定义排序那?因为按常规排序时,得到的序列是如下图第一行所示,

因为已经排好序,因此我们只需要考虑每个二元组的第二个元素即可,和《最长递归子序列》一样;但是如果像下图第二行,就不行了,如果两个二元组的第一个元素相等,只考虑每个二元组的第二个元素,找出的结果可能不符合,因为题目要求外层的信封的宽度和高度都大于里层的信封的高度和宽度,解决办法,将二元组的第一个元素按升序进行排序,如果两个二元组的第一个元素相等,则将这类二元组的第二个元素按降序排序即可,如下面第二张图:

代码

class Solution {
public:int maxEnvelopes(vector<vector<int>>& envelopes) {int n=envelopes.size();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> v;v.push_back(envelopes[0][1]);for(int i=1;i<n;i++){if(envelopes[i][1]>v.back())v.push_back(envelopes[i][1]);else{int left=0,right=v.size()-1;while(left<right){int mid=(left+right)>>1;if(v[mid]<envelopes[i][1])left=mid+1;elseright=mid;}v[left]=envelopes[i][1];}}return v.size();}
};
堆箱子

题目

思路

首先我们先对集合按区间左端点进行排序,因为如果不排序的话,比较两个区间左端点的大小是需要不断遍历集合的。

通过分析这道题,我们会发现就是要找出一个最长的升序序列即可,与《最长递增子序列》几乎是如出一辙,可以使用动态规划和定义排序+贪心+二分来解决,使用动态规划的时间复杂度是O(N^2)。

代码

class Solution {
public:int pileBox(vector<vector<int>>& box) {int n=box.size();sort(box.begin(),box.end());vector<int> dp(n);for(int i=0;i<n;i++){dp[i]=box[i][2];for(int j=0;j<i;j++){if(box[i][0]>box[j][0] && box[i][1]>box[j][1] && box[i][2]>box[j][2])dp[i]=max(dp[i],dp[j]+box[i][2]);}}return *max_element(dp.begin(),dp.end());}
};
可被三整除的最大和

题目

思路

贪心策略

如果我们通过拼凑若干个数来找到可被三整除的最大和是较为困难的,此时我们不妨尝试“正难则反”,先求出所有数的和,看是否能被三整除,如果能被三整除的话,所有数的和肯定是最大的值;如果不能被三整除的话,就删除某些数,如果总和模3余1,要么删除1个模3等于1的数,要么删除两个数的和模3等于2的数中最小的和次最小的数;如果总和模3余2,要么删除1个模等于2的数,要么删除两个数的和模3等于1的数中最小的和次最小的数。

寻找数组中最小和次最小的数,要么对数组排序的方式找,时间复杂度是O(N^logN),要么遍历整个数组,使用两个变量来记录数组中最小和次最小的数,时间复杂度是O(N),更优。

代码

class Solution {
public:int maxSumDivThree(vector<int>& nums) {const int INF=0x3f3f3f3f;int sum=0,x1=INF,x2=INF,y1=INF,y2=INF;for(int 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);}
};
距离相等的条形码

题目

思路

首先统计出现次数最多的数出现的次数,因为题目保证存在答案,因此这个次数的值肯定小于等于(数组大小n+1)/2的。

贪心策略

我们先把出现次数最多的数进行摆放,把出现次数最多的数摆放在奇数位置处,然后再摆放剩下的其余的数,只需要摆放两遍即可,先把所有的奇数位置处摆放完,再摆放偶数处的位置,这样能保证相邻位置的两个数是不相同的。

代码

class Solution {
public:vector<int> rearrangeBarcodes(vector<int>& barcodes) {unordered_map<int,int> hash;int maxVal=0,maxCount=0;for(int x:barcodes){if(maxCount<++hash[x]){maxCount=hash[x];maxVal=x;}}int n=barcodes.size();vector<int> ret(n);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>=n) index=1;ret[index]=a;index+=2;}}return ret;}
};
重构字符串

题目

思路

这一道题和上一道题几乎是一模一样的,不同之处是这道题不一定能成功重排字符。

首先统计出现次数最多的字符出现的次数,这个次数的值可能小于等于(数组大小n+1)/2,也可能大于(数组大小n+1)/2,此时返回空串。

贪心策略

我们先把出现次数最多的字符进行摆放,把出现次数最多的字符摆放在奇数位置处,然后再摆放剩下的其余的字符,只需要摆放两遍即可,先把所有的奇数位置处摆放完,再摆放偶数处的位置,这样能保证相邻位置的两个字符是不相同的。

代码

class Solution {
public:string reorganizeString(string s) {int n=s.size();int hash[26];char ch;int maxCount=0;for(char c:s){if(maxCount<++hash[c-'a']){maxCount=hash[c-'a'];ch=c;}}if(maxCount>(n+1)/2) return "";else{string str(n,' ');int index=0;for(int i=0;i<maxCount;i++){str[index]=ch;index+=2;}hash[ch-'a']=0;for(int i=0;i<26;i++){for(int j=0;j<hash[i];j++){if(index>=n) index=1;str[index]=i+'a';index+=2;}}return str;}}
};// class Solution {
// public:
//     string reorganizeString(string s) {
//         int n=s.size();
//         unordered_map<char,int> hash;
//         char ch;
//         int maxCount=0;
//         for(char c:s){
//             if(maxCount<++hash[c]){
//                 maxCount=hash[c];
//                 ch=c;
//             }
//         }
//         string str;
//         if(maxCount>(n+1)/2) return str;
//         else{
//             str.resize(n);
//             int index=0;
//             for(int i=0;i<maxCount;i++){
//                 str[index]=ch;
//                 index+=2;
//             }
//             hash.erase(ch);
//             for(auto& [t,k]:hash){
//                 for(int i=0;i<k;i++){
//                     if(index>=n) index=1;
//                     str[index]=t;
//                     index+=2;
//                 }
//             }
//             return str;
//         }
//     }
// };

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • pikachu文件下载
  • 详解爬虫使用代理ip的几种方案
  • 【GCC】结合GPT4 延迟梯度学习:公式推导及理论分析
  • 学习记录(11):训练图片分类的算法
  • 【linux】企业级linux内核优化方案,助你构建出高效、稳定且安全的Linux系统环境
  • MySQL深分页和浅分页
  • JVM详解(个人学习笔记)
  • 基于FPGA的数字信号处理(18)--半加器和全加器
  • 嵌入式网络调试命令 ifconfig 介绍及使用方法
  • 【五大海内外高校支持】2024年数字经济与计算机科学国际学术会议(DECS2024)
  • 壁纸头像小程序uniapp版(附源码)
  • YOLOv8新版本支持实时检测Transformer(RT-DETR)、SAM分割一切
  • nginx 代理 mysql 连接
  • 关于Redis的面试题
  • 企业如何构建全面的指标管理体系?
  • JS 中的深拷贝与浅拷贝
  • Cookie 在前端中的实践
  • Electron入门介绍
  • ES6系列(二)变量的解构赋值
  • ESLint简单操作
  • javascript 哈希表
  • JavaWeb(学习笔记二)
  • SwizzleMethod 黑魔法
  • win10下安装mysql5.7
  • 持续集成与持续部署宝典Part 2:创建持续集成流水线
  • 搭建gitbook 和 访问权限认证
  • 番外篇1:在Windows环境下安装JDK
  • 服务器从安装到部署全过程(二)
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 如何使用 JavaScript 解析 URL
  • 微服务框架lagom
  • 为什么要用IPython/Jupyter?
  • ​​​​​​​​​​​​​​Γ函数
  • ​14:00面试,14:06就出来了,问的问题有点变态。。。
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • (14)学习笔记:动手深度学习(Pytorch神经网络基础)
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (Oracle)SQL优化技巧(一):分页查询
  • (二)原生js案例之数码时钟计时
  • (翻译)Quartz官方教程——第一课:Quartz入门
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (原創) 系統分析和系統設計有什麼差別? (OO)
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .gitignore文件忽略的内容不生效问题解决
  • .helper勒索病毒的最新威胁:如何恢复您的数据?
  • .NET Reactor简单使用教程
  • .NET未来路在何方?
  • .NET下的多线程编程—1-线程机制概述
  • .NET业务框架的构建
  • .net中的Queue和Stack
  • @data注解_一枚 架构师 也不会用的Lombok注解,相见恨晚
  • @kafkalistener消费不到消息_消息队列对战之RabbitMq 大战 kafka
  • [ASP.NET 控件实作 Day7] 设定工具箱的控件图标
  • [BeginCTF]真龙之力
  • [BUUCTF 2018]Online Tool(特详解)