贪心系列专题篇四

​​​​​​​

目录

整数替换

解法一

解法二

俄罗斯套娃信封问题

堆箱子

可被三整除的最大和

距离相等的条形码

重构字符串


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

整数替换

题目

思路

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

解法一

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

代码

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;
//         }
//     }
// };

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】算法的时间复杂度与空间复杂度

计算机考研408-数据结构笔记本之——第一章 绪论 1.2 算法和算法评价 1.2.2 算法效率的度量 算法效率的度量是通过时间复杂度和空间复杂度来描述的。 1.时间复杂度 时间复杂度T(n)是事前预估算法时间开销T(n)与问题规模 n 的关系&#xff08;T 表示 “time”&#xff09;&a…

眼在手外-机器人坐标系与相机坐标系标定方法

1 眼在手外坐标系概述 实现机械臂和相机的手眼标定&#xff0c;就是要通过双目相机坐标系、机械臂坐标系和机械臂 末端执行器三者的坐标系转换&#xff0c;求出手眼转换矩阵。设双目相机坐标系为 Oc&#xff0c;标定板坐标 系为 Ow&#xff0c;末端执行器坐标系为 Oe&#xff0…

【C++】二维数组定义方式

二维数组有四种定义方式 1、数据类型 数组名[行数 ][ 列数 ]; 2、数据类型 数组名[ 行数 ][ 列数 ]{{数据1&#xff0c;数据2}&#xff0c;{数据3&#xff0c;数据4 }}; 3、数据类型 数组名[ 行数 ][ 列数 ]{数据1&#xff0c;数据2&#xff0c;数据3&#xff…

Java | Leetcode Java题解之第321题拼接最大数

题目&#xff1a; 题解&#xff1a; class Solution {public int[] maxNumber(int[] nums1, int[] nums2, int k) {int m nums1.length, n nums2.length;int[] maxSubsequence new int[k];int start Math.max(0, k - n), end Math.min(k, m);for (int i start; i < e…

04 表的操作

目录 创建查看修改删除 1. 创建 语法&#xff1a; CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储殷勤; 说明&#xff1a; field&#xff0c;表示列名 datatype&#xff0c;表示列的类型…

C++ 继承 派生类的拷贝构造

继承 派生类的拷贝构造构造顺序拷贝构造 引例1: 当子类,不自实现拷贝构造时,默认调用父类的拷贝构造引例2: 子类自实现拷贝构造,不做特殊处理时,只会调用父类的构造器.引例3: 显示的调用父类的拷贝构造器。案例: 内嵌函数的拷贝构造 引例1 :当内嵌子对象,子类不自实现拷贝构造时…

【java】单行注释(//)与多选注释(/* */)

文章目录 单行注释多行注释注意事项 在Java中&#xff0c;注释是用来给代码添加说明的&#xff0c;它不会被编译器执行。Java提供了两种主要的注释方式&#xff1a;单行注释和多行注释&#xff08;有时也称为块注释或块级注释&#xff09;。 单行注释 单行注释以两个正斜杠&…

数模评价决策类—熵权法

目录 文章目录 前言 一、熵权法是什么&#xff1f; 二、基本步骤 计算样本权重及熵权 总结 前言 前面我们学习了层次分析法和Topsis法&#xff0c;这两个方法都是偏主观的方法&#xff0c;这篇我们将运用较为客观的方法——熵权法&#xff0c;做出决策 一、熵权法是什么…

JavaWeb-HTML

一、HTML&CSS&JavaSript的作用: 1.HTML主要用于网页为主体结构的搭建&#xff1b; 2.CSS主要用于页面元素的美化 3.JavaScript主要用于页面元素的动态处理; 二、HTML HTML是Hyper Text Markup Language的缩写。意思是超文本标记语言。它的作用是搭建网页结构&…

2024死磕小红书,一定能赚到钱!

​2024死磕小红书&#xff0c;一定能赚到钱&#xff01;在文末领取小红书运营完全指南电子书 从2023年起&#xff0c;小红书这股热乎劲儿就像开了挂&#xff0c;突然间就成了人人想蹭的“显学”。大伙儿都想趁着平台红利期&#xff0c;分一杯羹。说来惭愧&#xff0c;我从2020年…

牛顿插值法代替泰勒公式

引入 例题 近似函数&#xff1a; 通过这个近似函数可以看出&#xff0c;若要证的函数超过二阶可导&#xff0c;那么就不适合用牛顿插值法代替泰勒公式 因为&#xff0c;后面的操作非常复杂&#xff0c;不划算了… 总结 我们可以通过牛顿插值法生成一个逼近曲线的直线&#xf…

使用Python库开发Markdown编辑器并将内容导出为图片

简介 在本文中&#xff0c;我们将探索如何使用Python的wxPython库开发一个Markdown编辑器应用程序。这个应用程序不仅能浏览和编辑Markdown文件&#xff0c;还可以将编辑的内容导出为PNG图片。 C:\pythoncode\new\markdowneditor.py 完整代码 import wx import markdown2 im…

【传知代码】实体关系抽取(论文复现)

当谈论信息提取领域的最前沿时&#xff0c;实体关系抽取无疑是其中一颗耀眼的明星。从大数据时代的信息海洋中提炼出有意义的关系&#xff0c;不仅是科技进步的体现&#xff0c;更是人类对知识管理和智能决策迫切需求的响应。本文将探索实体关系抽取的核心技术、应用场景及其在…

太阳光模拟器在光纤中的应用

概述 太阳光模拟器是一种重要的实验室设备&#xff0c;它能模拟太阳光的光谱、强度和角度分布&#xff0c;广泛应用于光纤通信、光电器件测试、太阳能研究等多个领域。通过模拟太阳光的光照条件&#xff0c;研究人员可以在实验室环境中对光电材料和器件进行性能测试和研究。 太…

二维码生成原理及解码原理

☝☝☝二维码配图 二维码 二维码&#xff08;Quick Response Code&#xff0c;简称QR码&#xff09;是一种广泛使用的二维条形码技术&#xff0c;由日本公司Denso Wave在1994年开发。二维码能有效地存储和传递信息&#xff0c;广泛应用于商品追溯、支付、广告等多个领域。二维…

c++入门基础(下篇)————引用、inline、nullptr

引用 引用的概念和定义 引⽤不是新定义⼀个变量&#xff0c;⽽是给已存在变量取了⼀个别名&#xff0c;编译器不会为引⽤变量开辟内存空间&#xff0c; 它和它引⽤的变量共⽤同⼀块内存空间。 类型& 引用别名 引用对象; 就像孙悟空也叫齐天大圣 猪八戒也叫天蓬元帅。…

Meinberg Lantime服务器监控指标解读

监控易是一款功能强大的IT基础设施监控软件&#xff0c;它能够实时监控各种IT设备的状态&#xff0c;提供全面的性能分析和告警通知服务。对于Meinberg Lantime服务器&#xff0c;监控易通过一系列监控指标&#xff0c;确保服务器的稳定运行和服务的可用性。 一、监控对象概述…

策略模式的一次应用

项目的需求是将一组图像按照相似度分类。 采用了模板匹配计算相似度的实现方式。 #include <opencv2/core.hpp> #include <openev2/core/utility.hpp> #include <opencv2/highqui.hpp> #include <openav2/imgproc.hpp> cv::Mat image matched; double …

Linux系统编程 --- 动静态库

一、回顾&#xff0c;制作一个库 libXXX.a --- 静态链接 libYYY.so --- 动态链接 设计一个库&#xff1a; 把我们提供的方法&#xff0c;给别人用&#xff1a; 1、把源文件直接给他 2、把我们的源代码打包成库 库 头文件。 原理&#xff1a;把所有的.o文件打包成.a文件也…

llama神经网络的结构,llama-3-8b.layers=32 llama-3-70b.layers=80; 2000汉字举例说明

目录 llama-3-8b.layers=32 llama-3-70b.layers=80 llama神经网络的结构 Llama神经网络结构示例 示例中的输入输出大小 实际举例说明2000个汉字文本数据集 初始化词嵌入矩阵 1. 输入层 2. 嵌入层 3. 卷积层 4. 全连接层 llama-3-8b.layers=32 llama-3-70b.laye…