首页 > 编程语言 >贪心算法专题(五)

贪心算法专题(五)

时间:2024-12-27 17:32:00浏览次数:8  
标签:index 专题 return int sum 算法 envelopes hash 贪心

目录

1. 整数替换

1.1 算法原理

1.1.1 解法一: 递归 + 记忆化搜索

1.1.2 解法二: 贪心

1.2 算法代码

2. 俄罗斯套娃信封问题

2.1 算法原理

2.1.1 解法一: 动态规划

 2.1.2 解法二: 重写排序规则 + 贪心 + 二分

2.2 算法代码

3. 可被三整除的最大和

3.1 算法原理

3.2 算法代码

4. 距离相等的条形码

4.1 算法原理

4.2  算法代码

5. 重构字符串

5.1 算法原理

5.2 算法代码


1. 整数替换

397. 整数替换 - 力扣(LeetCode)

1.1 算法原理

1.1.1 解法一: 递归 + 记忆化搜索

返回值: 数值 n 转为 1 的最小操作数

函数体:

  1. if(n == 偶) return dfs(n / 2) + 1;
  2. if(n == 奇) return min(dfs(n - 1), dfs(n + 1)) + 1;

记忆化搜索(减少重复递归带来的时间消耗):

  • 使用 memo 记录计算过的值(Map 创建)

1.1.2 解法二: 贪心

解法二: 贪心

将 n 以二进制的形式看待:

  1. n 为偶数: 除2
  2. n 为奇数: 分类讨论

1.2 算法代码

class Solution {
    /**
     * 贪心
     * @param n_
     * @return
     */
    public int integerReplacement(int n_) {
        long n = n_;
        int ret = 0;
        while(n != 1) {
            if(n == 3) return ret += 2;// 特殊情况
            if(n % 2 == 0) n /= 2;
            else {
                // 贪心
                long val = n % 4;
                if(val == 3) n += 1;
                else n -= 1;
            }
            ret++;
        }
        return ret;
    }
    /**
     * 递归 + 记忆化搜索
     */
    Map<Long, Integer> memo = new HashMap<>();
    public int integerReplacement1(int n) {
        return dfs(n);
    }
    public int dfs(long n) {
        if(memo.containsKey(n)) return memo.get(n);
        if(n == 1) return 0;
        if(n % 2 == 0) {
            memo.put(n, dfs(n / 2) + 1);
            return memo.get(n);
        }
        else {
            memo.put(n, Math.min(dfs(n + 1), dfs(n - 1)) + 1);
            return memo.get(n);
        }
    }
}

2. 俄罗斯套娃信封问题

354. 俄罗斯套娃信封问题 - 力扣(LeetCode)

2.1 算法原理

2.1.1 解法一: 动态规划

预处理: 按照左端点进行排序
问题转换: 最长套娃序列的长度 => 最长递增递增子序列

  • 状态表示dp[i]: 以 i 位置的信封为结尾的所有套娃序列中, 最长套娃序列的长度
  • 状态转移方程: dp[i] =  max(dp[j] + 1)  j < i && e[j][0] > e[i][0] && e[j][1] > e[i][1]
  • 初始化: Arrays.fill(dp, 1);
  • 建表顺序: 从左往右
  • 返回值: dp 表中的最大值

 2.1.2 解法二: 重写排序规则 + 贪心 + 二分

问题转换: 最长递增子序列

  • 重写排序规则:

1. 当左端点不同时, 左端点从小到大进行排序

2. 当左端点相同时, 右端点从大到小进行排序

  • 贪心

<最长递增子序列> (根据右端点的值, 寻找最长递增子序列)

  • 二分

<最长递增子序列> (二分寻找插入位置 => O(log N))

2.2 算法代码

class Solution {
    /**
     * 重写排序规则 + 贪心 + 二分
     * @param envelopes
     * @return
     */
    public int maxEnvelopes(int[][] envelopes) {
        // 重写排序规则
        Arrays.sort(envelopes, (a, b) -> {
            // 左端点不同时, 按照左端点升序排序
            if(a[0] != b[0]) return a[0] - b[0];
                // 左端点相同时, 按照右端点逆序排序
            else return b[1] - a[1];
        });
        int n = envelopes.length;
        List<Integer> list = new ArrayList<>();
        list.add(envelopes[0][1]);
        for(int i = 1; i < n; i++) {
            // 比最后一个数还大, 直接插入
            if(envelopes[i][1] > list.get(list.size() - 1)) {
                list.add(envelopes[i][1]);
                continue;
            }
            int left = 0, right = list.size() - 1;
            // 二分插入 + 贪心
            while(left < right) {
                int mid = left + (right - left) / 2;
                // 贪心
                if(list.get(mid) < envelopes[i][1]) left = mid + 1;
                else right = mid;
            }
            list.set(left, envelopes[i][1]);
        }
        return list.size();
    }

    /**
     * 动态规划 O(N^2) 超时
     * @param envelopes
     * @return
     */
    public int maxEnvelopes1(int[][] envelopes) {
        // 1. 排序(左端点)
        Arrays.sort(envelopes, (a, b) -> a[0] - b[0]);
        int n = envelopes.length, ret = 1;
        int[] dp = new int[n];
        // 初始化
        Arrays.fill(dp, 1);
        for(int i = 1; i < n; i++) {
            int a = envelopes[i][0], b = envelopes[i][1];
            for(int j = i - 1; j >= 0; j--) {
                if(a > envelopes[j][0] && b > envelopes[j][1]) dp[i] = Math.max(dp[i], dp[j] + 1);
            }
            ret = Math.max(ret, dp[i]);
        }
        return ret;
    }
}

3. 可被三整除的最大和

1262. 可被三整除的最大和 - 力扣(LeetCode)

3.1 算法原理

解法一: 正难则反 + 贪心

正难则反: 求出所有数的累加和(sum), 删除一些尽可能小的数, 使得 sum % 3 == 0

贪心:

分类讨论: 

x : % 3 == 1 的数
y : % 3 == 2 的数

  • 情况1 : sum % 3 == 0  直接返回 sum 即可.
  • 情况2 : sum % 3 == 1, 则 sum 可由以下两种情况构成:

1. 由 x1 和 (sum - x1) 构成, 并且 x1 % 3 == 1, (sum - x1) % 3 == 0
2. 由 y1, y2 和 (sum - y1 - y2) 构成, 并且 y % 3 == 2((y1 + y2) % 3 == 1), (sum - y1 - y2) % 3 == 0

返回 Math.max(sum - x1, sum - y1 - y2)

  • 情况3 : sum % 3 == 2, 则 sum 可由以下两种情况构成:

1. 由 y1 和 (sum - y1) 构成, 并且 y1 % 3 == 2, (sum - y1) % 3 == 0

2. 由 x1, x2 和 (sum - x1 - x2) 构成, 并且 x % 3 == 1((x1 + x2) % 3 == 2), (sum - x1 - x2) % 3 == 0

 返回 Math.max(sum - y1, sum - x1 - x2)

3.2 算法代码

class Solution {
    public int maxSumDivThree(int[] nums) {
        // 防溢出
        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 Math.max(sum - x1, sum - y1 - y2);
        }else {
            return Math.max(sum - y1, sum - x1 - x2);
        }
    }
}

4. 距离相等的条形码

1054. 距离相等的条形码 - 力扣(LeetCode)

4.1 算法原理

贪心 + 模拟

贪心策略: 对同一批数, 隔一个位置放一个

先处理出现次数最多的那个数.

4.2  算法代码

class Solution {
    public int[] rearrangeBarcodes(int[] barcodes) {
        Map<Integer, Integer> hash = new HashMap<>();
        // 记录出现最多的数
        int maxVal = 0, maxCnt = 0;
        for(int x : barcodes) {
            hash.put(x, hash.getOrDefault(x, 0) + 1);
            int cnt = hash.get(x);
            if(cnt > maxCnt) {
                maxCnt = cnt;
                maxVal = x;
            }
        }
        // 处理出现最多的数
        int index = 0;
        for(int i = 0; i < maxCnt; i++) {
            barcodes[index] = maxVal;
            index += 2;
        }
        // 处理剩余的数
        hash.remove(maxVal);
        for(int x : hash.keySet()) {
            int cnt = hash.get(x);
            while(cnt-- != 0) {
                if(index >= barcodes.length) index = 1;
                barcodes[index] = x;
                index += 2;
            }
        }
        return barcodes;
    }
}

5. 重构字符串

767. 重构字符串 - 力扣(LeetCode)

5.1 算法原理

贪心. 

本题思想与上题一致, 唯一需要多处理的一点是: 结果可能不存在.

5.2 算法代码

class Solution {
    public String reorganizeString(String s) {
        char maxVal = ' ';
        int maxCount = 0, n = s.length();
        int[] hash = new int[26];
        for(int i = 0; i < n; i++) {
            char ch = s.charAt(i);
            hash[ch - 'a']++;
            if(hash[ch - 'a'] > maxCount) {
                maxCount = hash[ch - 'a'];
                maxVal = ch;
            }
        }
        // 处理出现次数最多的字符
        int index = 0;
        char[] ret = new char[n];
        while(maxCount-- != 0) {
            if(index >= n) return "";
            ret[index] = maxVal;
            index += 2;
        }
        // 处理剩余字符
        hash[maxVal - 'a'] = 0;
        for(int i = 0; i < 26; i++) {
            char ch = (char)('a' + i);
            while(hash[i]-- != 0) {
                if(index >= n) index = 1;
                ret[index] = ch;
                index += 2;
            }
        }
        return new String(ret);
    }
    public String reorganizeString1(String s) {
        Map<Character, Integer> hash = new HashMap<>();
        char maxVal = 'x';
        int maxCount = 0, n = s.length();
        for(int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            hash.put(ch, hash.getOrDefault(ch, 0) + 1);
            if(hash.get(ch) > maxCount) {
                maxCount = hash.get(ch);
                maxVal = ch;
            }
        }
        int index = 0;
        char[] ret = new char[n];
        while(maxCount-- != 0) {
            if(index >= n) return "";
            ret[index] = maxVal;
            index += 2;
        }
        hash.remove(maxVal);
        for(char ch : hash.keySet()) {
            int count = hash.get(ch);
            while(count-- != 0) {
                if(index >= n) index = 1;
                ret[index] = ch;
                index += 2;
            }
        }
        return new String(ret);
    }
}

END

标签:index,专题,return,int,sum,算法,envelopes,hash,贪心
From: https://blog.csdn.net/2401_83595513/article/details/144113587

相关文章

  • 3种算法实现Python3数组的旋转
    Python3实现旋转数组的3种算法下面是Python3实现的旋转数组的3种算法。一、题目给定一个数组,将数组中的元素向右移动k个位置,其中k是非负数。例如:输入:[1,2,3,4,5,6,7]和k=3输出:[5,6,7,1,2,3,4]解释:向右旋转1步:[7,1,2,3,4,5,6]向右旋转2步:[6,7,1......
  • 手撕算法-严刑拷打
    题目背景:给定m台“相同”机器(或工作线程、工人等),以及n个“任务”或“工作”,每个任务都有一个执行时间。我们需要将这n个任务分配到这m台机器上,使得所有任务都执行完所需的总时间(完工时间)最小,即要找出一个最优的分配方案,让“最后一台机器”完成任务的时间尽可能早。......
  • 贪心
    贪心邻项交换法对于两项\((a_x,b_x),(a_y,b_y)\),我们比较谁在前面谁在后面,只需要比较仅有这两项的情况下,谁前谁后是更优的。满足\(a_i\geb_i\)。若\(x\)在\(y\)前,所需要的血量为\(\max(a_x,b_x+a_y)\)。若\(y\)在\(x\)前,所需要的血量为\(\max(a_y,b_y+a_x)\)。......
  • 模拟退火算法
    模拟退火方法,全称为模拟退火算法(SimulatedAnnealing,SA),是一种基于概率的通用优化算法,其思想来源于固体退火原理。以下是对模拟退火方法的详细解释:一、基本原理模拟退火算法模拟了物理中固体退火的过程来搜索问题的最优解。在固体退火过程中,固体被加热至高温后缓慢冷却,内部粒子从......
  • 算法网关视频分析网关小知识:如何对视频分析系统实施冗余设计以提高稳定性?
    在城市交通管理中,视频分析系统扮演着至关重要的角色,它不仅需要实时监控和分析交通流量,还需要在各种复杂环境下保持稳定运行。为了确保视频分析系统在面对设备故障、网络中断、电源波动等不可预见情况时仍能保持高可用性,实施冗余设计成为了提高系统稳定性的关键策略。以下是一些有......
  • 别再夹灰了!这份Java架构六大专题面试宝典值得你好好刷一刷
    Java面试是一个老生常谈的问题。每年到了金三银四&金九银十这种跳槽黄金季就会有一大批程序员出来面试找工作。流程就是熟悉的网上开始找面试题,面试手册,面试宝典,一收藏就是一大把,看到什么都觉得Nice,看几眼之后就收藏夹吃灰,对面试其实起不到什么实际性帮助。但其实只要你不做收......
  • K-means算法分析与实践
    一、聚类分析概述定义:根据“物以类聚”原理,将本身尚未归类的样本根据多个维度(属性)聚集成不同的组,这样的一组数据对象的集合叫做簇或群组。经过聚类划分后的群组特性目标:属于同一群组的样本之间彼此足够相似;属于不同群组的样本应该足够不相似;聚类与分类的区别:聚类没......
  • 代码随想录算法训练营第五十九天|dijkstra(堆优化版)精讲、Bellman_ford
    前言打卡代码随想录算法训练营第49期第五十九天⚆_⚆(˘❥˘)(•̀⌓•)シ(人•͈ᴗ•͈)♡♡首先十分推荐学算法的同学可以先了解一下代码随想录,可以在B站卡哥B站账号、代码随想录官方网站代码随想录了解,卡哥清晰易懂的算法教学让我直接果断关注,也十分有缘和第49期的......
  • 【递归,搜索与回溯算法 & floodfill 算法】深入理解 floodfill 算法:floodfill 算法小专
         图像渲染  题目解析     算法原理     解法:暴搜      模拟过程     递归过程:     回溯过程:    处理细节问题   但是如果在上述矩阵的情况下,给我们的color不是2,而是1,也就是......
  • ISP算法之BNR降噪(Bayer域)
    概述BNR(BayerNoiseReduction)即Bayer域降噪算法。对于噪声的分类如下表所示:高斯噪声( Gaussian)高斯噪声也被称为热噪声,通常是由于电路系统中自由电子的热运动,这种噪声幅度分布服从高斯分布,而它的功率谱密度又是均匀分布的。散粒噪声量子涨落现象,​量子涨落也是一种涨......