刷题之贪心3

前言

大家好,我是jiantaoyab,这篇文章将给大家介绍贪心算法和贪心算法题目的练习和解析,贪心算法的本质就是每一个阶段都是局部最优,从而实现全局最优。加上这篇文章一共有30道贪心题目了,加油!

坏了的计算器

image-20240323084010973

题目分析

image-20240323090707048

所以,只要tar<= sV,无论是奇数还是偶数都加1,当tar>sV时,奇数+1,偶数/2。

代码

class Solution {
public:
    int brokenCalc(int startValue, int target) {
      int count = 0;
      while(target > startValue)
      {
  
        if(target % 2 == 0) target /= 2;
        else target += 1;
        count++;
      }
      //剩下target <= startValue 都是+1的
      //所以startValue - target 统计出有多少个1
      return count + startValue - target;
    }
};

合并区间

image-20240323091948868

题目分析

这种问题叫区间问题,一般先排序。这里用左端点排序。

image-20240323095428846

证明:能够合并的区间都是连续的

image-20240323100506949

代码

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
      vector<vector<int>> ret;
      //排序
      sort(intervals.begin(), intervals.end());
      int left = intervals[0][0], right = intervals[0][1];
      for(int i = 0; i < intervals.size(); i++)
      {
        int l = intervals[i][0], r = intervals[i][1];
        if(l <= right)
        {
          //有重叠部分
          right = max(right, r);
        }
        else
        {
          //没有重叠部分
          ret.push_back({left, right});
          left = l;
          right = r;
        }
      }
      ret.push_back({left, right});
      return ret;

    }
};

无重叠区间

image-20240323100619988

贪心的策略如果有重叠区间就移除右断点大的区间,如果没重叠区间的话说明这个区间不会和后面的区间重叠就保留下来,接着用下一个区间去和后面的区间判断。

代码

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        int count = 0;
        sort(intervals.begin(), intervals.end());
        int left = intervals[0][0], right = intervals[0][1];
        for(int i = 1; i < intervals.size(); i++)
        {
          int l = intervals[i][0], r = intervals[i][1];
          if(l < right) 
          {
            //有重叠部分
            count++;
            right = min(right, r);
            
          }
          else  
          {
            //没有重叠部分
            right = r;
          }
        }
        return count;
    }
};

用最少数量的箭引爆气球

image-20240325084609342

题目分析

这道题目和合并区间求的不一样,我们这里求的是交集。

image-20240325091858577

代码

class Solution {
public:
    int findMinArrowShots(vector<vector<int>>& points) {
        sort(points.begin(), points.end());
        int n = points.size();
        int ret = 1; //默认是有一个区间去和下一个区间进行比较
        int right = points[0][1];
        for(int i = 0; i < n; i++)
        {
          int l = points[i][0], r = points[i][1];
          if(l <= right)
          {
            //有交集
            right = min(r, right);
          }
          else
          {
            //没有交集
            ret++;
            right = r;
          }
        }
        return ret;
      
    }
};

整数替换

image-20240325092631599

题目分析

image-20240325102306024

代码

贪心思想来做

class Solution {
public:
    int integerReplacement(int n) {
      int count = 0;
      while(n != 1)
      {
        if(n % 2 == 0) 
        {
          n /= 2;
          count++;
        }
        else
        {
          if(n == 3)
          {
              n = 1;
              count += 2;
          }         
          else if(n % 4 == 3) //说明是...11
          {
             n = n / 2 + 1;
             count += 2;
          }
          else //....01
          {
            count += 2;
            n /= 2;
          }

        }
      }
      return count;
    }
};

用记忆化搜索做

class Solution {
  unordered_map<int, int> hash;
public:
    int dfs(long long n)
    {
      if(hash[n]) return hash[n];
      if(n == 1)
      {
        hash[1] = 0;
        return 0;
      } 
      if(n % 2 == 0)
      {
        hash[n] = 1 + dfs(n / 2);
        return hash[n];
      }
      else
      {
        hash[n] = 1 + min(dfs(n + 1), dfs(n - 1));
        return hash[n];
      }
    }
    int integerReplacement(int n) {
      return dfs(n);
    }
};

俄罗斯套娃信封问题

image-20240325103044807

题目分析

image-20240325110947735

代码

不是很理解的可以看看第一篇贪心算法

class Solution {
public:
    int maxEnvelopes(vector<vector<int>>& envelopes) {
        sort(envelopes.begin(), envelopes.end(), [&](const vector<int>&v1, const vector<int>&v2)
        {
          return v1[0] != v2[0] ? v1[0] < v2[0] : v1[1] >  v2[1];
        });

        vector<int> ret;
        ret.push_back(envelopes[0][1]);

        for(int i = 1; i < envelopes.size(); i++)
        {
          int r = envelopes[i][1];
          if(r > ret.back())
          {
            ret.push_back(r);
          }
          else
          {
            int left = 0, right = ret.size() - 1;
            while(left < right)
            {
              int mid = (left + right) / 2;
              if(ret[mid] >= r) right = mid;
              else left = mid + 1;
            }
            ret[left] = r;
          }
        }

        return ret.size();
    }
};

可被三整除的最大和

image-20240326191035787

题目分析

image-20240326193532737

引入一个新问题,如何求出最小值和次小值

image-20240326193915292

代码

class Solution {
public:
    int maxSumDivThree(vector<int>& nums) {
       const int INF = 0x3f3f3f3f;
       int x1 = INF, x2 = INF, y1 = INF, y2 = INF;
       int sum = 0;
       for(auto 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 max(sum - x1, sum - y1 - y2);
       else return max(sum - y1, sum - x1 - x2);
    }
};

距离相等的条形码

image-20240326195602785

题目分析

把数字间隔1个数字放,先放次数最多的,放下的数字的顺序怎么放都可以。

证明

题目一定是有解的,那么出现最多的那个数字不能超过 n + 1 / 2次,因为如果有n个抽屉,放n + 1 个东西,必然有一个抽屉放2个,分情况讨论。

如果最多出现的数字次数刚好是 n + 1 / 2,那么剩下的随便放,都不相邻

如果出现的数字次数小于 n + 1 / 2,那更不可能相邻了,如果有相邻的说明这个数字出现的次数一定比 这个数字出现的次数多,那它就是最多出现次数的数字了,所以先放次数最多的,放下的数字的顺序怎么放都可以。

代码

class Solution {
public:
    vector<int> rearrangeBarcodes(vector<int>& barcodes) {
      unordered_map<int, int> hash;
      int n = barcodes.size();
      int max_val = 0, max_count = 0;
      for(auto x : barcodes)
      {
        if(max_count < ++hash[x])
        {
          max_val = x;
          max_count = hash[x];
        }
      }

      vector<int> ret(n);
      int index = 0;
      //先放出现次数最多的数字
      for(int i = 0; i < max_count; i++)
      {
        ret[index] = max_val;
        index += 2;
      }

      //处理剩下的数字
      hash.erase(max_val);
      for(auto& [x, y] : hash)
      {
        for(int i = 0; i < y; i++)
        {
          if(index >= n)index = 1;
           ret[index] = x;
            index += 2;
        }
      }
      return ret;
    }
};

重构字符串

image-20240326203040064

代码

这道题目和上一题的区别就是这道题目可能是没有解的。

class Solution {
public:
    string reorganizeString(string s) {
      int hash[26] = {0};
      int n = s.size();
      char max_ch = ' ';
      int max_count = 0;
      for(const auto ch : s)
      {
        if(max_count < ++hash[ch - 'a'])
        {
          max_count = hash[ch - 'a'];
          max_ch = ch;
        }
      }
      if(max_count > (n + 1) / 2 ) return "";
      string ret(n, ' ');
      int index = 0;
      //先放最多的英文
      for(int i = 0; i < max_count; i++)
      {
        ret[index] = max_ch;
        index += 2;
      }
      hash[max_ch - 'a'] = 0;
      //放剩下的英文
     for(int i = 0; i < 26; i++)
     {
        for(int j = 0; j < hash[i]; j++)
        {
          if(index >= n) index = 1;
          ret[index] = 'a' + i;
          index += 2;
        }
     }
      return ret;
    }
};

  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值