Skip to content

贪心

使所有字符相等的最小成本

2712. 使所有字符相等的最小成本 - 力扣(LeetCode)

该题目贪心加脑筋急转弯

从前往后遍历,我们只关心相邻两个字符是否相同,如果不相同则需要反转其中一侧,这里需要使用贪心思想,我们每次反转时,比较一下反转左侧需要的成本小,还是反转右侧需要的成本小,每次反转成本小的那一侧,这样遍历时,每次我们都会将相邻的字符变成相同的,需要注意的是反转操作只会改变 s[i−1] 与 s[i] 是否相等,不会改变其他相邻字符是否相等(相等的都反转还是相等,不相等的都反转还是不相等),所以每对相邻字符其实是互相独立的,我们只需要分别计算这些不相等的相邻字符的最小成本,即 min(i,n−i),累加即为答案。

java
class Solution {
    public long minimumCost(String s) {
        char[] ch = s.toCharArray();
        long res = 0;
        int len = s.length();
        for(int i = 1;i < len;i++){
            if(ch[i-1] != ch[i]){
                res += Integer.min(i,len - i);
            }
        }
        return res;
    }
}

我们并不需要做实质性的反转,即不需要操作s字符串,因为我们操作是会影响 [0,i -1] 或者 [ i - n - 1]区间的所有字符串的,反转之后,相等的还是相等,不相等的还是不相等,当我们遍历到时还是需要操作,所有我们只需要记录答案即可,而不要真正的反转。举例:当在判断下标为 0 和 1 时,它们不相等我们会将其反转成相等的,而在判断下标为1 和 2 时,我们会将其反转成相等的,这样0,1,2都是相等的了,以此类推最后全部变成相等的

最大子数组和

53. 最大子数组和 - 力扣(LeetCode)

贪心点:如果遍历到当前点时,前面所有累加和小于0,那么先直接将累加和重置成0,因为负数只会将当前点的值变得更小,所有我们先将累加和重置成0

每次累加完当前值时我们需要将其与当前最大值相比较,防止当前结果是最大值时将其错过

java
class Solution {
    public int maxSubArray(int[] nums) {
        int sum = 0;
        int res = Integer.MIN_VALUE;
        for(int num : nums){
            sum+=num;
            res = Integer.max(res,sum);
            if(sum < 0){
                sum = 0;
            }
        }
        return res;
    }
}

跳跃游戏

55. 跳跃游戏 - 力扣(LeetCode)

我们设置一个cover值,表示遍历到当前位置时,我们所能向前跳跃的最远距离,因为本题没有次数限制,只需要判断是否能到达终点即可,所以当我们每遍历一个位置时,我们更新现在cover的最大值,如果其覆盖到了终点则直接返回true,否则遍历结束返回false

java
class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length == 1){
            return true;
        }
        int cover = nums[0];
        for(int i = 0; i <= cover; i++ ){
            cover = Integer.max(cover,i + nums[i]);
            if(cover >= nums.length - 1){
                return true;
            }
        }
        return false;
    }
}

跳跃游戏II

45. 跳跃游戏 II - 力扣(LeetCode)

这题相比于上一题,要难好多啊。

我们定义一个int curr 表示当前我们所能到达的范围(范围右边界)定义 int next 表示在该范围内我们找到的下一个能跳得范围最广的右边界,注意:curr和next表示的是数组当中的索引值,而不是范围的大小,我们在当前范围寻找下个范围最大值时,如果发现已经找到一个范围,它涵盖了数组右边界即n - 1,那我们直接在该范围的基础上,跳跃到终点。如果我们遍历到当前右边界还未发现能数组右边界的范围,则我们需要更新现在的范围为下一个范围的最大值(就是在遍历上一个范围计算出来的范围),这个更新现在的范围的操作就相当于是在跳跃,跳跃的点就是拥有下一个范围的最大值的点

java
class Solution {
    public int jump(int[] nums) {
        if(nums.length == 1){
            return 0;
        }
        int res = 0;
        int curr = 0;
        int next = 0;
        for(int i = 0; i < nums.length; i++){
            next = Integer.max(next,i + nums[i]);
            if(next >= nums.length-1){
                return res+1;
            }
            if(i == curr){
                curr = next;
                res++;
            }
        }
        return res;
    }
}

加油站

134. 加油站 - 力扣(LeetCode)

好难啊😡😣

在遍历的时候,我们保存累计油量的开始位置,并且累加计算从前面到现在为止,我们总共消耗多少油,以及还剩多少油,如果剩下的油已经变成负数了,那我们从下一位置开始重新计算剩余油量(总共消耗多少油一直累加),当循环遍历结束后,如果总共消耗油量小于0,说明整体来看,耗油量大于加油量,直接返回-1,否则就是一定有解,由前面所示,如果剩下的油已经变成负数了,那我们从下一位置开始重新计算剩余油量,所以我们保存累计油量的开始位置的值就一定是答案,返回保存累计油量的开始位置即可

java
class Solution {
    public int canCompleteCircuit(int[] gas, int[] cost) {
        int[] arr = new int[gas.length];
        for(int i = 0;i < gas.length; i++){
            arr[i] = gas[i] - cost[i];
        }
        int sum = 0;
        int len = arr.length;
        int t = 0;
        int res = 0;
        for(int i = 0; i<len ;i++){
            sum += arr[i];
            t+=arr[i];
            if(sum < 0){
                sum = 0;
                res = i + 1;
            }
        }
       return t >= 0?res:-1;
    }
}