栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【代码随想录】【LeetCode】自学笔记 10 - 贪心算法

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【代码随想录】【LeetCode】自学笔记 10 - 贪心算法

贪心算法介绍

贪心算法一般分为如下四步:

将问题分解为若干个子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
其实这个分的有点细了,真正做题的时候很难分出这么详细的解题步骤,可能就是因为贪心的题目往往还和其他方面的知识混在一起。
贪心没有套路,说白了就是常识性推导加上举反例。

455.分发饼干
// 
class Solution {
public:
    bool canJump(vector& nums) {
        int cover = 0;
        if (nums.size() == 1) return true; // 只有一个元素,就是能达到
        for (int i = 0; i <= cover; i++) { // 注意这里是小于等于cover
        【第一次出现】不断更新 for 的终点,类似回溯or动态规划?
            cover = max(i + nums[i], cover);
            if (cover >= nums.size() - 1) return true; // 说明可以覆盖到终点了//-1,注意此时是下标,所以-1
        }
        return false;
    }
};
45.跳跃游戏II
// // 以最小的步数增加最大的覆盖范围,直到覆盖范围覆盖了终点,这个范围内最小步数一定可以跳到,不用管具体是怎么跳的,不纠结于一步究竟跳一个单位还是两个单位。
// //一步一步(i)遍历整个数组[0~倒数第二位],在每一步记录能到的最大的终点,这个终点最大只能是数组终点,如果不够大,后续步数会追上,此时ans ++
注意和前一题(跳跃游戏1)的 for 等等参数都不同,需要再次对比着看
// class Solution {
// public:
//     int jump(vector& nums) {
//         int ans = 0;            // 记录走的最大步数
//         int curDistance = 0;    // 当前覆盖的最远距离下标
//         int nextDistance = 0;   // 下一步覆盖的最远距离下标
//         for (int i = 0; i < nums.size() - 1; i++) { // 注意这里是小于nums.size() - 1,这是关键所在
//             nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
//             if (i == curDistance) {                 // 遇到当前覆盖的最远距离下标
//                 curDistance = nextDistance;         // 更新当前覆盖的最远距离下标
//                 ans++;
//             }
//         }
//         return ans;
//     }
// };
1005. K 次取反后最大化的数组和
// // 第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
// // 第二步:从前向后遍历,遇到负数将其变为正数,同时 K--
// // 第三步:如果 K 还大于0,那么反复转变数值最小的元素,将 K 用完(若 k 大于0,只需考虑是奇数的情况)
// // 第四步:求和
// //局部最优:让绝对值大的负数变为正数,当前数值达到最大,整体最优:整个数组和达到最大。局部最优可以推出全局最优

// class Solution {
// static bool cmp(int a, int b) {//【第一次出现! (默认private?)static cmp 】
//     return abs(a) > abs(b);//将 sort 的规则改成绝对值的降序排列(从大到小)//return a& A, int K) {
//         sort(A.begin(), A.end(), cmp);       // 第一步
//         for (int i = 0; i < A.size(); i++) { // 第二步
//             if (A[i] < 0 && K > 0) {
//                 A[i] *= -1;
//                 K--;
//             }
//         }
//         if (K % 2 == 1) A[A.size() - 1] *= -1; // 第三步
//         int result = 0;
//         for (int a : A) result += a;        // 第四步
//         return result;
//     }
// };
class Solution{
    static bool cmp (int a, int b) {
        return abs(a)>abs(b);///函数不像 if 必须有大括号
    }
    public:
    int largestSumAfterKNegations(vector& nums, int K){
        sort(nums.begin(), nums.end(), cmp);
        for(int i = 0; i < nums.size(); i ++){
            if (nums[i] < 0 && K > 0) {//还是加上{}保险。。。
                nums[i] *= -1;///&& K > 0
                K--;
            }
        }

        if (K%2 == 1) nums[nums.size()-1] *= -1;
        int sum = 0;
        for(int i = 0; i < nums.size(); i++){
            sum += nums[i];
        }
          //for (int a : nums) sum += a;        //更简单!       
        return sum;
    }   
};
134. 加油站
class Solution {
public:
    int canCompleteCircuit(vector& gas, vector& cost) {
        int curSum = 0;
        int totalSum = 0;
        int start = 0;
        for (int i = 0; i < gas.size(); i++) {
            curSum += gas[i] - cost[i];
            totalSum += gas[i] - cost[i];
            if (curSum < 0) {   // 当前累加res t[i]和 curSum一旦小于0
                start = i + 1;  // 起始位置更新为i+1
                curSum = 0;     // curSum 从0开始
            }
        }
        if (totalSum < 0) return -1; // 说明怎么走都不可能跑一圈了
        return start;
    }
};
//首先如果总油量减去总消耗大于等于零那么一定可以跑完一圈,说明 各个站点的加油站 剩油量rest [i]相加一定是大于等于零的。
//如果gas 的总和小于cost 总和,那么无论从哪里出发,一定是跑不了一圈的
//why ??需要重理解。目前理解只能是举不出反例。
//有一个环形路上有n个站点; 每个站点都有一个好人或一个坏人; 好人会给你钱,坏人会收你一定的过路费,如果你带的钱不够付过路费,坏人会跳起来把你砍死; 问:从哪个站点出发,能绕一圈活着回到出发点?(vectorint 不能直接相减)
135. 分发糖果
// 这道题目一定是要确定一边之后,再确定另一边,例如比较每一个孩子的左边,然后再比较右边,如果两边一起考虑一定会顾此失彼。
// 此时局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果
// ///相同评分的相邻孩子,可以获得不同数量的糖果(从而可以2,1,1,1,2,减少花费的糖果总数
// 再确定左孩子大于右孩子的情况(从后向前遍历)
// 遍历顺序这里有同学可能会有疑问,为什么不能从前向后遍历呢?
// 因为如果从前向后遍历,根据 ratings [i + 1] 来确定 ratings [i] 对应的糖果,那么每次都不能利用上前一次的比较结果了。局部最优:取candyVec [i + 1] + 1 和 candyVec [i] 最大的糖果数量,保证第i个小孩的糖果数量即大于左边的也大于右边的。全局最优:相邻的孩子中,评分高的孩子获得更多的糖果。
// 局部最优可以推出全局最优。
// 所以就取 candyVec [i + 1] + 1 和 candyVec [i] 最大的糖果数量,candyVec [i]只有取最大的才能既保持对左边candyVec [i - 1]的糖果多,也比右边 candyVec [i + 1]的糖果多。

// 这在leetcode上是一道困难的题目,其难点就在于贪心的策略,如果在考虑局部的时候想两边兼顾,就会顾此失彼。
// 那么本题我采用了两次贪心的策略:
// 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
// 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
// 这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。
class Solution {
public:
    int candy(vector& ratings) {
        vector candyVec(ratings.size(), 1);//【第一次见】初始化vector的初始值和大小为1
        // 从前向后
        for (int i = 1; i < ratings.size(); i++) {//右大改右,跳过第一个
            if (ratings[i] > ratings[i - 1]) candyVec[i] = candyVec[i - 1] + 1;
        }
        // 从后向前
        for (int i = ratings.size() - 2; i >= 0; i--) {//左大改左,跳过最后一个
            if (ratings[i] > ratings[i + 1] ) {candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);}    
        }
        // 统计结果
        int result = 0;
        for (int i = 0; i < candyVec.size(); i++) result += candyVec[i];
        return result;
    }
};
860.柠檬水找零
// 情况一:账单是5,直接收下。
// 情况二:账单是10,消耗一个5,增加一个10
// 情况三:账单是20,优先消耗一个10和一个5,如果不够,再消耗三个5
// 此时大家就发现 情况一,情况二,都是固定策略,都不用我们来做分析了,而唯一不确定的其实在情况三。
// 所以局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零。

class Solution {
public:
    bool lemonadeChange(vector& bills) {
        int five = 0, ten = 0, twenty = 0;
        for (int bill : bills) {
            // 情况一
            if (bill == 5) five++;
            // 情况二
            if (bill == 10) {
                if (five <= 0) return false;
                ten++;
                five--;
            }
            // 情况三
            if (bill == 20) {
                // 优先消耗10美元,因为5美元的找零用处更大,能多留着就多留着
                if (five > 0 && ten > 0) {
                    five--;
                    ten--;
                    twenty++; // 其实这行代码可以删了,因为记录20已经没有意义了,不会用20来找零
                } else if (five >= 3) {
                    five -= 3;
                    twenty++; // 同理,这行代码也可以删了
                } else return false;
            }
        }
        return true;
    }
};
406.根据身高重建队列
在这里插入代码片
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/782817.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号