贪心算法
1、基本概念
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,
从而希望导致结果是全局最好或最优的算法。
贪心算法解决问题的策略是:做出选择时,每次都选择对当前状态最优的解,而不考虑整个问题的解空间。它通常用来解决最优化问题,如最小生成树、哈夫曼编码等。
2、步骤
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来解问题的一个解
3、使用条件
利用贪心法求解的问题应具备如下2个特征:
1、贪心选择性质
一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。这就是贪心选择性质。对于一个具体问题,要确定它是否具有贪心选择性质,必须证明每一步所作的贪心选择最终导致问题的整体最优解。
2、最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用贪心法求解的关键所在。在实际应用中,至于什么问题具有什么样的贪心选择性质是不确定的,需要具体问题具体分析。
局部最优解:观察问题是否可以通过选择当前看起来最优的解决方案来逐步构建全局最优解。
问题分解:分析问题是否可以分解成较小的子问题,且这些子问题的最优解能够直接用来构建原问题的最优解。
4、常见的贪心策略类型
- 选择最大(或最小)元素:在解决一些优化问题时,优先选择当前最大或最小的元素
- 资源分配:尽可能地利用可用资源达到最优解,例如,在预算限制下最大化收益。
- 任务调度:在任务规划或调度问题中,基于某种优先级(如最早完成时间、最小持续时间等)来选择任务。
- 集合覆盖:在需要覆盖整个集合的问题中,每一步选择覆盖最多未覆盖元素的选项。
- 动态规划简化:对于某些可以使用动态规划解决的问题,贪心算法提供了更高效的解决方案,尤其是当问题满足某种特定结构时。
5、案列
5.1、分配问题
输入输出样例
Input: [1,2], [1,2,3]
Output: 2
题解:
1 int findContentChildren(vector<int>& children, vector<int>& cookies) {
2 sort(children.begin(), children.end());
3 sort(cookies.begin(), cookies.end());
4 int child = 0, cookie = 0;
5 while (child < children.size() && cookie < cookies.size()) {
6 if (children[child] <= cookies[cookie]) ++child;
7 ++cookie;
8 }
9 return child;
10 }
题目描述:
输入输出样例
Input: [1,0,2]
Output: 5
题解:
1 int candy(vector<int>& ratings) {
2 int size = ratings.size();
3 if (size < 2) {
4 return size;
5 }
6 vector<int> num(size, 1);
7 for (int i = 1; i < size; ++i) {
8 if (ratings[i] > ratings[i-1]) {
9 num[i] = num[i-1] + 1;
10 }
11 }
12 for (int i = size - 1; i > 0; --i) {
13 if (ratings[i] < ratings[i-1]) {
14 num[i-1] = max(num[i-1], num[i] + 1);
15 }
16 }
17 return accumulate(num.begin(), num.end(), 0); // std::accumulate 可以很方便地求和
18 }
5.2、区间问题
题目描述
输入输出样例
Input: [[1,2], [2,4], [1,3]]
Output: 1
题解
1 int eraseOverlapIntervals(vector<vector<int>>& intervals) {
2 if (intervals.empty()) {
3 return 0;
4 }
5 int n = intervals.size();
6 sort(intervals.begin(), intervals.end(), [](vector<int> a, vector<int> b) {
7 return a[1] < b[1];
8 });
9 int total = 0, prev = intervals[0][1];
10 for (int i = 1; i < n; ++i) {
11 if (intervals[i][0] < prev) {
12 ++total;
13 } else {
14 prev = intervals[i][1];
15 }
16 }
17 return total;
18 }
5.3、买卖股票
题目描述
给你一个整数数组 prices
,其中 prices[i]
表示某支股票第 i
天的价格。在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
输入输出样例
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
总利润为 4 + 3 = 7 。
题解
本题采用局部最优的方法,在遍历天数的过程中,时刻确认下一天和当天之间的利润,并且保证相加的时候,利润不可小于0,。其实本题可以抽象把价格数组,直接变成利润数组,转化为数组和这样的题目。因为只要保证当天存在利润,那么往后累加就一定是最大的利润
1 class Solution {
2 public:
3 int maxProfit(vector<int>& prices) {
4 int result = 0;
5 for(int i = 0;i<prices.size()-1;i++){
6 result += max(prices[i+1]-prices[i],0);
7 }
8 return result;
9 }
10 };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律