赞
踩
目录
贪心策略的百度解释是:
在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解 。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择 。
基本上所有的贪心算法都可以用回溯来解决(全排列),就是说如果真的想不到好的贪心策略去实现,全排列也可以解决这个算法问题,只是时间复杂度会非常高(>O(n!) && <O(n∗n!))
所以说贪心是一种优化算法,是在已经能解决问题的基础上,对题目本身的底层逻辑进行分析,之后找到优化策略去优化算法的时间复杂度。
贪心算法其实并没有严格意义上的解题思路
如何证明贪心策略是正确的呢?任何贪心策略的提出都是先提出再证明。先使用全排列的方法获取正确答案,再和贪心策略获得的结果进行比较,如果不等说明贪心策略出现错误
字典序最小
题目
给定一个由字符串组成的数组strs,必须把所有的字符串拼接起来,返回所有拼接结果中,字典序最小的结果。
示例 1:
思路
贪心策略:
代码
class Solution { public: std::string lowestString(std::vector<std::string> strs){ if(strs.empty()){ return ""; } std::sort(strs.begin(), strs.end(), [](std::string &l , std::string & r){ return (l + r) < (r + l); }); std::string ans ; for (auto & str : strs) { ans += str; } return ans; } };
类似题目
零钱问题
题目
小明是一个销售员,客人在他的地方买了东西,付给了小明一定面值的钱之后,小明需要把多余的钱退给客人。客人付给了小明n,小明的东西的售价为m,小明能退回给客人的面额只能为[100,50,20,10,5,2,1]的组合。现在小明想要使找出去纸币张数最小,请返回这个最小值。
示例 1:
思路
策略:
代码
class Solution { private: std::vector<int> comb {100, 50, 20, 10, 5, 2, 1}; public: int coinProblem(int n, int m) { int mon = n - m; // 还需要找多少钱 int res = 0; // 一共找出去的钱的张数 for (int i : comb){ res += mon / i; // 当前面额最大可以找多少钱 mon = mon % i; // 还剩下多少钱需要找 } return res; } };
类似题目
股票问题(最多持有一支,可以买卖无限次)
题目
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
示例 1:
示例 2:
示例 3:
提示:
解析
分析:
思路:
代码
class Solution { public: int maxProfit(vector<int>& prices) { int res = 0; for (int i = 1; i < prices.size(); ++i) { if(prices[i] - prices[i - 1] > 0){ res += prices[i] - prices[i - 1]; } } return res; } };
小船过河
题目
给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。
每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。
返回 承载所有人所需的最小船数 。
示例 1:
示例 2:
示例 3:
思路
典型的 N:M类型问题,类似的问题还有,N个进程每一类型用时不同,M个相同CPU,求最少时间,本质上也是求最小资源使用。类似的可以参考后面题目<项目调度器>
分析:
解题步骤:
代码
class Solution { public: int numRescueBoats(vector<int>& people, int limit) { int N = people.size(); std::sort(people.begin(), people.end()); if(people[N - 1] > limit){ return -1; } int ans = 0, l = 0, r = N - 1, sum = 0; while (l <= r){ // 当 people [l] > limit/2 时,ans+= (r-l == 0 ? 1:r-l) if (people [l] > limit/2.) { ans+= (r-l == 0 ? 1:r-l); break; } // 当人数总数为奇数时处理边界条件 sum = l == r ? people[l] : people[l] + people[r]; if(sum > limit){ r--; }else{ l++; r--; } ans++; } return ans; } };
任务调度器
题目
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
示例 1:
输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。
示例 2:
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
诸如此类
示例 3:
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A
提示:
思路
分析:
可以分为两种情况:
步骤:
PS: 上述过程简化的公式可以为:
代码
class Solution{ static bool cmp(int &a, int &b){ return a > b; } public: int leastInterval(std::vector<char> &tasks, int n){ if(!n){ return tasks.size(); } std::vector<int> nums(26, 0); for(char tast : tasks){ nums[tast - 'A'] += 1; } std::sort(nums.begin(), nums.end(), cmp); int total = (n - 1) * (nums[0] - 1) + 1; for (int i = 1; i < nums.max_size(); ++i) { if(nums[i] == nums[0]){ total++; } } return total > tasks.size() ? total : tasks.size(); } };
摆动序列
题目
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。
思路
PS: 这个题目改个名字就很简单了:判断一个数组波峰波谷的和
分析:
代码
class Solution { public: int wiggleMaxLength(vector<int>& nums) { if (nums.size() <= 1) return nums.size(); int curDiff = 0; // 当前一对差值 int preDiff = 0; // 前一对差值 int result = 1; // 记录峰值个数,序列默认序列最右边有一个峰值 for (int i = 0; i < nums.size() - 1; i++) { curDiff = nums[i + 1] - nums[i]; // 出现峰值 if ((curDiff > 0 && preDiff <= 0) || (preDiff >= 0 && curDiff < 0)) { result++; preDiff = curDiff; } } return result; } };
class Solution { public int wiggleMaxLength(int[] nums) { int n = nums.length; if (n < 2) { return n; } // 因为有边界节点(起点、终点)因此最少一个长度 int up = 1; int down = 1; for (int i = 1; i < n; i++) { if (nums[i] > nums[i - 1]) { up = down + 1; } if (nums[i] < nums[i - 1]) { down = up + 1; } } return Math.max(up, down); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。