【算法】C++贪心算法解题(单调递增数字、坏了的计算器、合并区间)

文章目录

前言

关于贪心算法/策略的概念、理解性问题在:

【算法】贪心算法解析:基本概念、策略证明与代码例题演示


算法题

1.单调递增的数字


思路

题目要求:找到 满足单调递增的 <= n的最大数字;

比如:

  • n = 1330, ret = 1229

  • n = 241, ret = 239

  • n = 1001, ret = 0999 -> 999

  • n = 233, ret = 233

    • 不难看出来,当n的位数第一次出现递减时,ret 的该位应该降位;
    • 但降位之前应该确保n的递减位前面没有值相同的,所以应该先向前检索

则总结出思路:

  1. 首先找出首个递减的位置: while(i + 1 < m && s[i] <= s[i+1]) i++;
  2. 随后判断递减位置之前,是否存在连续相同的数:while(i - 1 >= 0 && s[i] == s[i-1]) i--;
  3. 随后递减位-1,将后面的所有数位填9,即为最大的。

代码

cpp 复制代码
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string s = to_string(n);

        int i = 0, m = s.size();
        // 找第一个递减的位置
        while(i + 1 < m && s[i] <= s[i+1]) i++;
        // n 本身就是递增数
        if(i + 1 == m) return n;

        // 如果在首位递减的数 有连续相同的:回推
        while(i - 1 >= 0 && s[i] == s[i-1]) i--;

        // 递减位-1,后面全填9
        s[i]--;
        for(int j = i + 1; j < m; ++j) s[j] = '9';
        
        return stoi(s);
    }
};

2.坏了的计算器

思路

从 target 回溯到 startValue,以找到最小操作数:

  1. 回溯操作:

    • 如果 target 大于 startValue:
      • 如果 target 是偶数,将 target 除以 2。
      • 如果 target 是奇数,将 target 加 1(使其变为偶数,以便下一步除以 2)。
    • 每次操作计数 ret 加 1。
  2. 处理剩余部分:

    • 当 target 小于或等于 startValue 时,将 startValue 减去 target 并加到 ret 中,这部分是直接的加法操作。

最终返回 ret 加上 startValue 和 target 之间的差值,即 startValue - target


代码

cpp 复制代码
class Solution {
public:
    int brokenCalc(int startValue, int target) {
        // 正难则反:从target -> startValue 的最小操作数
        int ret = 0;
        while(target > startValue)
        {
            if(target % 2 == 0) // 偶数除以2
                target /= 2;
            else // 奇数+1
                target += 1; 
            ret++;
        }

        return ret + startValue - target;
    }
};

3.合并区间


思路

  1. 排序:

    • 按照每个区间的左端点进行排序,以确保处理区间时按顺序考虑它们的重叠情况。
  2. 初始化:

    • 设置初始区间的左端点 left 和右端点 right 为第一个区间的端点。
  3. 遍历和合并:

    • 从第二个区间开始,遍历所有区间。
    • 对于每个区间,检查它的左端点是否小于或等于当前的右端点:
      • 如果是,说明区间有重叠,更新右端点为当前区间的右端点和之前右端点中的最大值。
      • 如果不是,说明区间不重叠,将当前区间 [left, right] 添加到结果中,并更新 left 和 right 为当前区间的端点。
  4. 添加最后一个区间:

    • 遍历完成后,将最后的区间 [left, right] 添加到结果中。

代码

cpp 复制代码
class Solution {
public:
    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>> ret;
        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 { // 无重叠
                ret.push_back({left, right});
                left = a, right = b;
            }
        }
        ret.push_back({left, right}); // 最后一个区间
        return ret;
    }
};

相关推荐
启观川8 小时前
数据结构与算法 -第 3 章 常用算法-动态规划
数据结构·笔记·python·算法
码爸8 小时前
排序算法介绍
python·算法·排序算法
sm_926787058 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
by209998 小时前
C++ 多态:同一个基类接口,调用不同的函数
开发语言·c++·经验分享·多态
colinu8 小时前
C++ 异步任务与线程生命周期:`std::async`、`future`、`thread`、`jthread` 与协作式退出
java·jvm·c++
动词ing9 小时前
【题目练习】算法 组合总和||
算法
wuminyu9 小时前
ForkJoinPool窃取线程在扫描其他WorkQueue时的随机采样算法与Phase线程挂起唤醒机制解析
java·linux·c语言·jvm·c++
longlongzihan9 小时前
链表找环:Floyd 判圈算法(LeetCode 141 & 142 )
c++·算法·leetcode·链表
专业程序开发源9 小时前
flask家电故障预测系统92491-计算机课程设计/毕业设计
vscode·python·sql·算法·flask·课程设计
liulilittle9 小时前
Linux 下 select 测试函数
linux·服务器·网络·数据库·c++·select·c