赞
踩
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。贪心算法并没有固定的套路,唯一的难点就是如何通过局部最优,推出整体最优。最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧
贪心算法一般分为如下四步:
只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了
题目描述:
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。
对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
示例 1:
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
可以尝试使用贪心策略,先将饼干数组和小孩数组排序。
然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量。
一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式
一定要 for 控制 胃口,里面的 if 控制饼干
- class Solution {
- public:
- //贪心算法,使用大饼干先满足大胃口的原则来实现
- int findContentChildren(vector<int>& g, vector<int>& s) {
- sort(g.begin(),g.end());//对胃口和饼干都进行排序
- sort(s.begin(),s.end());
- int index = s.size() - 1;//这里采用下标的形式来实现
- int result = 0;//定义结果
- //我们从后向前遍历,实现大饼干满足大胃口的原则
- for(int i = g.size() - 1;i >= 0;i--){
- //如果满足了条件,饼干下标减一,结果收集
- if(index >= 0 && s[index] >= g[i]){
- result++;
- index--;
- }
- }
- return result;
- }
- };
- class Solution {
- public:
- //贪心算法实现,小饼干喂小胃口的人,注意下标的选取就好
- int findContentChildren(vector<int>& g, vector<int>& s) {
- sort(g.begin(),g.end());
- sort(s.begin(),s.end());
- int index = 0,result = 0;
- for(int i = 0;i < s.size();i++){
- if(index < g.size() && g[index] <= s[i]){
- result++;
- index++;
- }
- }
- return result;
- }
- };
题目描述:
如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为摆动序列。第一个差(如果存在的话)可能是正数或负数。少于两个元素的序列也是摆动序列。
例如, [1,7,4,9,2,5] 是一个摆动序列,因为差值 (6,-3,5,-7,3) 是正负交替出现的。相反, [1,4,7,2,5] 和 [1,7,4,5,5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
给定一个整数序列,返回作为摆动序列的最长子序列的长度。 通过从原始序列中删除一些(也可以不删除)元素来获得子序列,剩下的元素保持其原始顺序。
示例 1:
贪心算法:
局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。
整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。
实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)
这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点
本题要考虑三种情况:
本题异常情况的本质,就是要考虑平坡, 平坡分两种,一个是 上下中间有平坡,一个是单调有平坡
- class Solution {
- public:
- int wiggleMaxLength(vector<int>& nums) {
- if(nums.size() <= 1)return nums.size();
- int prediff = 0;//前一对差值
- int curdiff = 0;//当前一对差值
- int result = 1;//结果
- //这里注意,i遍历到nums.size()-1,因为默认数组最右边一个序列有1,
- for(int i = 0;i < nums.size()-1;i++){
- curdiff = nums[i+1] - nums[i];//当前一对差值计算
- //出现波动,就会记录
- if((prediff <= 0 && curdiff > 0)||(prediff >= 0 && curdiff < 0)){
- result++;//结果改变
- prediff = curdiff;//只有摆动变化记录更改prediff值
- }
- }
- return result;
- }
- };
动态规划:
当前考虑的这个数,要么是作为山峰(即 nums[i] > nums[i-1]),要么是作为山谷(即 nums[i] < nums[i - 1])。
dp[i][0]
,表示考虑前 i 个数,第 i 个数作为山峰的摆动子序列的最长长度dp[i][1]
,表示考虑前 i 个数,第 i 个数作为山谷的摆动子序列的最长长度则转移方程为:
dp[i][0] = max(dp[i][0], dp[j][1] + 1)
,其中0 < j < i
且nums[j] < nums[i]
,表示将 nums[i]接到前面某个山谷后面,作为山峰。dp[i][1] = max(dp[i][1], dp[j][0] + 1)
,其中0 < j < i
且nums[j] > nums[i]
,表示将 nums[i]接到前面某个山峰后面,作为山谷。初始状态:
由于一个数可以接到前面的某个数后面,也可以以自身为子序列的起点,所以初始状态为:dp[0][0] = dp[0][1] = 1
。
- class Solution {
- public:
- int dp[1005][2];
- int wiggleMaxLength(vector<int>& nums) {
- memset(dp,0,sizeof dp);
- dp[0][0] = dp[0][1] = 1;
- for(int i = 1;i < nums.size();i++){
- dp[i][0] = dp[i][1] = 1;
- for(int j = 0;j < i;j++){
- if (nums[j] > nums[i]) dp[i][1] = max(dp[i][1], dp[j][0] + 1);
- }
- for (int j = 0; j < i; ++j) {
- if (nums[j] < nums[i]) dp[i][0] = max(dp[i][0], dp[j][1] + 1);
- }
- }
- return max(dp[nums.size() - 1][0],dp[nums.size() - 1][1]);
- }
- };
题目描述:
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
暴力解法:第一层 for 就是设置起始位置,第二层 for 循环遍历数组寻找最大值
- class Solution {
- public:
- int maxSubArray(vector<int>& nums) {
- int result = INT32_MIN;
- int count = 0;
- for(int i = 0;i < nums.size();i++){
- count = 0;
- for(int j = i;j < nums.size();j++){
- count += nums[j];
- result = count > result ? count : result;
- }
- }
- return result;
- }
- };
贪心算法:
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
局部最优的情况下,并记录最大的“连续和”,可以推出全局最优。
区间的终止位置,其实就是如果 count 取到最大值了,及时记录下来了,区间终止位置立刻记录
- class Solution {
- public:
- //
- int maxSubArray(vector<int>& nums) {
- int result = INT32_MIN;//定义一个最小值
- int count = 0;//这个是总和
- for(int i = 0;i < nums.size();i++){
- count += nums[i];//计算总和
- if(count > result){
- result = count;//如果总和大于结果就去更新,更新最大值
- }
- if(count < 0){
- count = 0;//重置最大子序列初始位置,小于零就重新定位
- }
- }
- return result;
- }
- };
动态规划:
- class Solution {
- public:
- int maxSubArray(vector<int>& nums) {
- if(nums.size() == 0)return 0;
- vector<int>dp(nums.size(),0);//dp[i]表示包括i之前的最大连续子序列和
- dp[0] = nums[0];
- int result = dp[0];
- for(int i = 1;i < nums.size();i++){
- dp[i] = max(dp[i-1]+nums[i],nums[i]);//状态转移公式
- if(dp[i] > result)result = dp[i];//result 保存dp[i]的最大值
- }
- return result;
- }
- };
题目描述:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
贪心算法:
假如第 0 天买入,第 3 天卖出,那么利润为:prices[3] - prices[0]。
相当于(prices[3] - prices[2]) + (prices[2] - prices[1]) + (prices[1] - prices[0])。
此时就是把利润分解为每天为单位的维度,而不是从 0 天到第 3 天整体去考虑,收集正利润的区间,就是股票买卖的区间,而我们只需要关注最终利润,不需要记录区间
- class Solution {
- public:
- //把整个利润去拆分成每一个利润的和形式,取最大利润就是求所有正利润就好
- int maxProfit(vector<int>& prices) {
- int result = 0;
- //这里注意i的取值从1开始取
- for(int i = 1;i < prices.size();i++){
- result += max(prices[i] - prices[i-1],0);//两个位置的价格差值,和0对比求得正利润即可
- }
- return result;
- }
- };
动态规划:
- class Solution {
- public:
- int maxProfit(vector<int>& prices) {
- // dp[i][1]第i天持有的最多现金
- // dp[i][0]第i天持有股票后的最多现金
- int n = prices.size();
- vector<vector<int>>dp(n,vector<int>(2,0));
- dp[0][0] -= prices[0];// 持股票
- for(int i = 1;i < prices.size();i++){
- dp[i][0] = max(dp[i-1][1] - prices[i],dp[i-1][0]);
- // 第i天持股票所剩最多现金 = max(第i-1天持股票所剩现金, 第i-1天持现金-买第i天的股票)
- dp[i][1] = max(dp[i-1][1],dp[i-1][0] + prices[i]);
- // 第i天持有最多现金 = max(第i-1天持有的最多现金,第i-1天持有股票的最多现金+第i天卖出股票)
- }
- return max(dp[n-1][0],dp[n-1][1]);
- }
- };
贪心算法理论基础:每一阶段的局部最优,得到全局最优的解决方法,无固定套路
分发饼干:大饼干喂胃口大的孩子,全局最优就是需要喂饱更多的小孩,先给饼干和小孩胃口排序,然后从后向前遍历小孩数组,用大饼干来喂饱大胃口小孩,并且统计数量,注意遍历饼干技巧,可以采用下标来自减方式实现分发饼干,这里的Index从后向前,可以大饼干喂大胃口或者小饼干喂小胃口
摆动序列:是指两个相邻的元素之差是正负交替的一组序列,使用贪心算法来实现,注意两端是需要算做一个,我们需要记录前一个和当前的差值,条件判断注意需要确保两个差值的正负不同号即可,pre=cur是关键,进行一组就是关键,
最大子序和:贪心算法这里需要注意一个细节我们最开始需要定义一个最小值,再定义每个数组和去,思想逻辑是如果数组和是小于0我们抛弃这个数组和置为0,还要更新最大数组和的一个操作
买卖股票的最佳时机II:我们只要想到利润可以拆分成多个利润相加的模式就可以解决,只要正利润就好,得到最后想要的结果,可以使用动态规划来实现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。