[贪心算法] 加油站 && 单调递增的数字 && 坏了的计算器 && 合并区间 && 无重叠区间

发布于:2025-03-27 ⋅ 阅读:(25) ⋅ 点赞:(0)

1.加油站

在这里插入图片描述

暴力解法:两层for循环,(先写一下,优化版本就是根据暴力解法写出来的)
优化:当从a位置出发到f位置的时候发现油变成负数了,那就说明从a位置到f位置这个区间内,所有的点都无法到达,所以我们直接从f的下一个位置开始出发寻找,这样时间复杂度就变低了(贪心
在这里插入图片描述
这里的diff表示gas[i]-cost[i]

class Solution {
public:
    int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
        int n=gas.size();
        for(int i=0;i<n;i++)
        {
            int total=0,step=0;
            for(step=0;step<n;step++)
            {
                int index=(i+step)%n;
                total=total+gas[index]-cost[index];
                if(total<0)  break;
            }
            if(total>=0) return i;
            i+=step;
        }
        return -1;
    }
};

2.单调递增的数字

在这里插入图片描述
暴力:同样用两层for循环即可
优化:在这里插入图片描述
我们只需要把数字成变小趋势的前一个数字-1,后面的数据全改成9即可;
但是要注意,当成变小趋势的前一个数字不止一个的时候,就要把最前面的数字-1,
在这里插入图片描述

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string num=to_string(n);
        int size=num.size();
        int i=0;
        //找到第一个变化的数字
        while(i+1<size() && num[i]<=num[i+1])
            i++;
        if(i+1==size)   return n;//说明这个数就是递增的
        //找到第一位
        while(i-1>=0 &7 num[i]==num[i-1])
            i--;

        //该数值
        s[i]--;
        for(int j=i+1;j<n;j++)
        {
            s[j]='9';
        }
        return stoi(num);
    }
};

3.坏了的计算器

在这里插入图片描述


> 正向求解:贪心:如果小于目标值就*2,大于目标值就--; 举个例子5->8
>*2变成10,在--两次变成8需要三次操作,但是最优操作只需要两次,所以这个贪心是错误的。
> 根据正难则反,我们可以去反推,用target->startValue;那操作条件就变成了只能 进行÷2++> 因为整数的除法是向下取整的,所以碰到奇数的只能进行++操作; 分类讨论 当target>startValue 奇数偶数都是++
> 当target<startValue 奇数++ 偶数/2

class Solution {
public:
    int brokenCalc(int startValue, int target) {
        int ret=0;
        while(target>startValue)
        {
            if(target%2==0) target/=2;
            else    target+=1;
            ret++;
        }
        return ret+startValue-target;
    }
};

4.合并区间

在这里插入图片描述

合并区间: 把第一个位置的数设为left和right 要比较的数字左右两边设为a和b
如果有重叠部分:right>=a,那就把区间变成[left,max(right,b)]
如果没有重叠部分,直接把当前的数组加入到数组中,然后把left和right设置位置从a和b开始向后出发

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {

        //1.排序
        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(right>=a)//有重叠部分
            {
                //求并集
                right=max(right,b);
            }
            else
            {
                ret.push_back({left,right});
                left=a;
                right=b;
            }
        }
        //把最后一次也添加到里面
        ret.push_back({left,right});
        return ret;
    }
};

5.无重叠区间

在这里插入图片描述

和上题类似,但是这题的left我们不需要关注 如果有重叠right>a,就把右端点较大的值移除 如果没有重叠,right=b,继续向后出发

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        
        //1.排序
        sort(intervals.begin(),intervals.end());
        int left=intervals[0][0],right=intervals[0][1];
        int ret=0;
        //删除区间
        for(int i=1;i<intervals.size();i++)
        {
            int a=intervals[i][0],b=intervals[i][1];
            if(right>a)//有重复区间
            {
                //贪心:删除右端点大的区间
                right=min(right,b);
                ret++;
            }
            else
            {
                left=a;
                right=b;
            }
        }
        return ret;
    }
};