贪心算法并没有固定的套路,就是如何通过局部最优,推出整体最优。一般分为如下四步:

  1. 将问题分解为若干个子问题
  2. 找出适合的贪心策略
  3. 求解每一个子问题的最优解
  4. 将局部最优解堆叠成全局最优解

例一:455.分发饼干

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。

对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j] 。如果 s[j] >= g[i],我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

class Solution(object):def findContentChildren(self, g, s):""":type g: List[int]:type s: List[int]:rtype: int"""#胃口小的孩子先用小饼干满足res=0m=len(g)n=len(s)g.sort()s.sort()i=0j=0while j<n and i<m:if s[j]>=g[i]:res+=1i+=1j+=1else:j+=1return res

例二.376. 摆动序列

如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。

例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。

相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。

给你一个整数数组 nums ,返回 nums 中作为 摆动序列 的 最长子序列的长度 。

输入:nums = [1,7,4,9,2,5]
输出:6
解释:整个序列均为摆动序列,各元素之间的差值为 (6, -3, 5, -7, 3) 。

class Solution(object):def wiggleMaxLength(self, nums):""":type nums: List[int]:rtype: int"""# ##贪心算法# #局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。# #全局最优:删掉单调坡度上的节点(不包括单调坡度两端的节点),保留其他局部峰值##容易错在细节:怎么处理示例如1 2 3 4 5 和0 0 0 或者3 3 3 2 5#注意:这里讲的是子序列而不是连续子序列,注意区分#错误版本:1 7 4 9 2 5示例过不了,对于第一个数的处理和最后一个数的处理需要注意 对于重复数字处理需要注意# n=len(nums)# if n==1:#     return 1# if n==2:#     if nums[0]==nums[1]:#         return 1#     return 2# res=1 #初始化res=1的理由:只要序列不为空,res最小为1 # for i in range(1,n-1):#     if (nums[i+1]-nums[i]>0 and nums[i]-nums[i-1]<=0) or (nums[i+1]-nums[i]<0 and nums[i]-nums[i-1]>=0):#         res+=1#         # print(i)#         # print(res)# return res## 正确版本贪心 #对于3 3 3 2 5序列的处理n=len(nums)curdiff=0prediff=0 #对于第一个数的处理,[2,5]处理成[2,2,5],prediff=2-2=0 这样第一个2可以被作为一个局部峰值res=1for i in range(n-1):curdiff=nums[i+1]-nums[i]if (curdiff>0 and prediff<=0) or (curdiff<0 and prediff>=0):res+=1prediff=curdiffreturn res#动态规划 #难点:想不到用二维dp来做,对每个nums[i],用两个数来分别存储当它作为山谷与作为山峰时的摆动子序列的最长长度#陷在了dp[i]怎么来表示的困境,也没有想到对nums[i]分山谷和山峰两种情况考虑#dp[i][0],表示考虑前i个数,第i个数作为山谷的摆动子序列的最长长度#dp[i][1],表示考虑前i个数,第i个数作为山峰的摆动子序列的最长长度dp=[[0 for _ in range(2)] for _ in range(n)]dp[0][0]=1 dp[0][1]=1for i in range(1,n):dp[i][0]=1dp[i][1]=1for j in range(i):if nums[i]<nums[j]: #求nums[i]作为山谷时的最大摆动子序列长度,使用dp[j][1]+1来更新dp[i][0]=max(dp[i][0],dp[j][1]+1) if nums[i]>nums[j]: #同理dp[i][1]=max(dp[i][1],dp[j][0]+1)return max(dp[n-1][0],dp[n-1][1]) #返回最后一个数分别作为山谷和山峰时的最长长度

例三.53. 最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

子数组 是数组中的一个连续部分。

示例 1:

输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。

class Solution(object):def maxSubArray(self, nums):""":type nums: List[int]:rtype: int"""## 动态规划#dp[i]:nums[i]位置的最大连续子数组和 dp[i]=max(dp[i-1]+nums[i],nums[i]) #递推公式中max(dp[i-1]+nums[i],nums[i])说明对比nums[i]和dp[i-1]+nums[i]哪个更大,其实和贪心算法的如果连续子数组和是负数,则抛弃重新计数一个道理n=len(nums)dp=[0]*(n)dp[0]=nums[0]res=nums[0]for i in range(1,n):dp[i]=max(dp[i-1]+nums[i],nums[i]) if dp[i]>res:res=dp[i]return res# "暴力算法."# "对于每个nums[i],j从每个i开始数、开始加,如果加出来的数大于之前加出来最大的数,就把最大数替换,这样循环下来就可以找到加出来的最大数"n=len(nums)if n==1:return nums[0]maxmum=-1e9for i in range(n):sum=0for j in range(i,n):sum+=nums[j]if sum>maxmum:maxmum=sumreturn maxmum#贪心算法#局部最优:如果连续子数组和是负数,则抛弃res=nums[0]n=len(nums)if n==1:return restmp=nums[0] #连续子数组和for i in range(1,n):if tmp>=0:tmp+=nums[i]else:tmp=nums[i] #如果连续子数组和是负数,从当前位置重新算if tmp>res:res=tmpreturn res

例四. 122. 买卖股票的最佳时机 II

给你一个整数数组 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 。

class Solution(object):def maxProfit(self, prices):""":type prices: List[int]:rtype: int"""#动态规划#对于每天i,可以选择买入、卖出、不操作三种选项,存在两种状态#dp[i][0]  第i天持有股票后的最多现金(不操作、买入)#dp[i][1]  第i天持有的最多现金(不操作、卖出)#对于每个i都更新这两个状态n=len(prices)dp=[[0 for _ in range(2)] for _ in range(n)]dp[0][0]-=prices[0]dp[0][1]=0for i in range(1,n):#第i天持股票所剩最多现金 = max(第i-1天持股票所剩现金, 第i-1天持现金-买第i天的股票)dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])# 第i天持有最多现金 = max(第i-1天持有的最多现金,第i-1天持有股票的最多现金+第i天卖出股票)dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])return max(dp[n-1][0],dp[n-1][1])#贪心算法#对每一个点,都有买入、卖出、不操作三种选项。山谷买入、山峰卖出、斜坡上不操作#需要考虑削平的山峰和山谷怎么处理 比如[2 1 2 1 0 0 1]#所以买入点是prices[i]<=prices[i-1] and prices[i]<prices[i+1]#卖出点是prices[i]>prices[i-1] and prices[i]>=prices[i+1]n=len(prices)res=0buy=0 #默认起点卖出,如果不是,buy后续会被覆盖sell=0for i in range(1,n-1):if prices[i]<=prices[i-1] and prices[i]<prices[i+1]:buy=iif prices[i]>prices[i-1] and prices[i]>=prices[i+1]:sell=ires+=prices[sell]-prices[buy]if prices[n-1]>prices[n-2]:sell=n-1res+=prices[sell]-prices[buy]return res

例五:55. 跳跃游戏

给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个下标。
输入:nums = [2,3,1,1,4]
输出:true
解释:可以先跳 1 步,从下标 0 到达下标 1, 然后再从下标 1 跳 3 步到达最后一个下标。

class Solution(object):def canJump(self, nums):""":type nums: List[int]:rtype: bool"""#贪心算法:#关键点在于:不用拘泥于每次究竟跳跳几步,而是看覆盖范围,覆盖范围内一定是可以跳过来的,不用管是怎么跳的。n=len(nums)startpos=0 #刚开始的出发点和能到达的最远点maxpos=nums[0]#首先,求出每个点可以到达的最远方farthestpos=[]for i in range(n):farthestpos.append(nums[i]+i)while maxpos<n-1: winmax=max(farthestpos[startpos:maxpos+1]) #框框里可以跳的最远的位置if maxpos>=winmax: #如果最远并没有比当前点更远return Falsemaxpos=winmaxstartpos=farthestpos[startpos:maxpos+1].index(winmax) #返回最大值位置return True#贪心算法简化版代码# 没必要用两次循环,可以简化为一次,并且可以直接返回i减少.index()函数的使用,节约用时n=len(nums)if n==1:return Truecover=0i=0#这里需要注意: python不支持动态修改for循环中变量,使用while循环代替while i<=cover:cover=max(nums[i]+i,cover)if cover>=n-1:return Truei+=1return False

例六.45. 跳跃游戏 II

给你一个非负整数数组 nums ,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

假设你总是可以到达数组的最后一个位置。
输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

class Solution(object):def jump(self, nums):""":type nums: List[int]:rtype: int"""#贪心算法#方法一思路:由于题目给出总是可以到达数组的最后一个位置,需要从后往前推到到下标为0的起点#能达到目标位置的节点越靠前,总跳数越少#cover记载跳的位置,每次都从前往后找到最靠前的能到达cover位置的下标,记载成为一跳n=len(nums)cover=n-1res=0while cover>0:for i in range(n):if nums[i]+i>=cover:cover=ires+=1breakreturn res# 方法二思路# 从前往后,移动下标达到了当前覆盖的最远距离下标时,步数就要加一n=len(nums)curpos=0 # 当前覆盖最远距离下标nextpos=0 #记录下一步最远距离下标res=0for i in range(n):nextpos=max(nums[i]+i,nextpos)if i==curpos: #遇到当前覆盖最远距离下标if curpos!=n-1: #如果当前覆盖最远距离下标不是终点,需要走下一步res+=1curpos=nextposif curpos>=n-1:breakelse:breakreturn res#方法二简化版#控制移动下标i只移动到n - 2的位置,所以移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不用考虑别的了#当移动下标i指向n - 2时,如果移动下标等于当前覆盖最大距离下标, 需要再走一步;#如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了n=len(nums)curpos=0 # 当前覆盖最远距离下标nextpos=0 #记录下一步最远距离下标res=0for i in range(n-1):nextpos=max(nums[i]+i,nextpos)if i==curpos: #遇到当前覆盖最远距离下标res+=1curpos=nextposreturn res

例七.1005. K 次取反后最大化的数组和

给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:

选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。

以这种方式修改数组后,返回数组 可能的最大和 。
输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。

#第一步:将数组按照绝对值大小从大到小排序,注意要按照绝对值的大小
#第二步:从前向后遍历,遇到负数将其变为正数,同时K--
#第三步:如果K还大于0,那么反复转变数值最小的元素,将K用完
#第四步:求和
class Solution(object):def largestSumAfterKNegations(self, nums, k):""":type nums: List[int]:type k: int:rtype: int"""#考虑(-1,2) k=2n=len(nums)nums.sort() #for i in range(n):if nums[i]<0:nums[i]=-nums[i]k-=1if k<=0:breaknums.sort()if k>0:if k%2==0:k=0else:nums[0]=-nums[0]return sum(nums)

例八.860. 柠檬水找零

在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。

每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。

注意,一开始你手头没有任何零钱。

给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账。如果你能给每位顾客正确找零,返回 true ,否则返回 false 。

输入:bills = [5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。

class Solution(object):def lemonadeChange(self, bills):""":type bills: List[int]:rtype: bool"""#贪心算法#局部最优:遇到账单20,优先消耗美元10,完成本次找零。全局最优:完成全部账单的找零cnt5=0cnt10=0cnt20=0for i in range(len(bills)):if bills[i]==5:cnt5+=1if bills[i]==10:cnt10+=1#找5块钱if cnt5<1:return Falseelse:cnt5-=1if bills[i]==20:cnt20+=1   #找15块钱 (5 5 5或10 5)if cnt10>=1 and cnt5>=1: #首选用10抵换,保留更多5cnt10-=1cnt5-=1elif cnt10<1 and cnt5>=3:cnt5-=3else:return Falsereturn True

例九:738. 单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x <= y 时,我们称这个整数是单调递增的。

给定一个整数 n ,返回 小于或等于 n 的最大数字,且数字呈 单调递增 。

示例 1:

输入: n = 10
输出: 9
示例 2:

输入: n = 1234
输出: 1234
示例 3:

输入: n = 332
输出: 299

class Solution(object):def monotoneIncreasingDigits(self, n):""":type n: int:rtype: int"""#输入100#输出90 应该是99#需要处理连续的比如4333 2118时,后面的数都要重新推翻num=int(n)n=list(str(n))i=len(n)-1factor=10while num//factor!=0:if n[i]<n[i-1]:# n[i]="9" #原来版本 但实际上不应该只是n[i]="9",而是n[i]之后也都是"9"n[i:]="9"* (len(n) - i)# print(int("".join(n[i-1:]))//factor-1)n[i-1]=str(int("".join(n[i-1:]))//factor-1)factor*=10i-=1return int("".join(n))############################################################### 优化版本a = list(str(n))for i in range(len(a)-1,0,-1):if int(a[i]) < int(a[i-1]):a[i-1] = str(int(a[i-1]) - 1)a[i:] = '9' * (len(a) - i)  #python不需要设置flag值,直接按长度给9就好了return int("".join(a))

例十:135. 分发糖果

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

你需要按照以下要求,给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

class Solution(object):def candy(self, ratings):""":type ratings: List[int]:rtype: int"""# ratings=[1,2,87,87,87,2,1] # ratings=[1,3,2,2,1]n=len(ratings)res=[1]*nfor i in range(1,n): #向右一遍,右边的值跟左边的值对比,修改右边的值if ratings[i-1]<ratings[i]:res[i]=res[i-1]+1# print(res)for i in range(n-2,-1,-1): #向左一遍,左边的数跟右边的数对比,修改左边的值if  ratings[i]>ratings[i+1]:res[i]=max(res[i+1]+1,res[i])# print(res)return sum(res)

例十一:406. 根据身高重建队列

假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。

请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。

class Solution(object):def reconstructQueue(self, people):""":type people: List[List[int]]:rtype: List[List[int]]"""# 先按照h维度的身高顺序从高到低排序。确定第一个维度# lambda返回的是一个元组:当-x[0](维度h)相同时,再根据x[1](维度k)从小到大排序people.sort(key=lambda x: (-x[0], x[1]))# print(people)que=[]## 由于此时people已经排序过了,身高从大到小,当身高相同时,按照根据x[1](维度k)从小到大排序for p in people:que.insert(p[1],p)return que

例十二:452. 用最少数量的箭引爆气球

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 。

class Solution(object):def findMinArrowShots(self, points):""":type points: List[List[int]]:rtype: int"""n=len(points)ans=1if n==1:return anspoints.sort(key=lambda x:x[1])# print(points)pos=points[0][1] #记录放箭位置 6i=1while i<n: # 当等于n时if pos<points[i][0]: #射出的剑射不到的位置pos=points[i][1]ans+=1# print("####")# print(i)# print(ans)i+=1return ans

例十三:435. 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

class Solution(object):def eraseOverlapIntervals(self, intervals):""":type intervals: List[List[int]]:rtype: int"""n=len(intervals)#先x[1]小的放前面,后x[0]大的放前面。当x[1]相等时,除掉x[0]小的,因为它覆盖范围更广#例子[[1,2],[2,3],[3,4],[1,3]] intervals.sort(key=lambda x:(x[1],x[0])) #sort后[[1, 2], [1, 3], [2, 3], [3, 4]]ans=0i=1pos=intervals[0][1]while i<n:if pos>intervals[i][0]:ans+=1else:pos=intervals[i][1]i+=1return ans

例十四:763. 划分字母区间

字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
输入:S = “ababcbacadefegdehijhklij”
输出:[9,7,8]
解释:
划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
每个字母最多出现在一个片段中。
像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。

class Solution(object):def partitionLabels(self, s):""":type s: str:rtype: List[int]"""#s="vhaagbqkaq"cnt=[0]*26n=len(s)for ss in s: #预处理,先数每个字母有几个cnt[ord(ss)-ord('a')]+=1# print(cnt)tmp=[s[0]]ans=[]count=0for i in range(n):if not tmp:ans.append(count)count=0 ##tmp.append(s[i])cnt[ord(s[i])-ord('a')]-=1if cnt[ord(s[i])-ord('a')]==0:tmp.remove(s[i])# print(tmp)else:if s[i] not in tmp:cnt[ord(s[i])-ord('a')]-=1tmp.append(s[i])else: #in tmpcnt[ord(s[i])-ord('a')]-=1# print(tmp)if cnt[ord(s[i])-ord('a')]==0:tmp.remove(s[i]) #python中remove函数用于移除列表中某个值的第一个匹配项#tmp里面也仅有一个匹配项,所以可以效果是移除tmp里面的s[i]count+=1ans.append(n-sum(ans))return ans

例十五:56. 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

class Solution(object):def merge(self, intervals):""":type intervals: List[List[int]]:rtype: List[List[int]]"""# intervals=[[1,4],[2,3]]n=len(intervals)# intervals=[[1,3],[2,6],[8,10],[2,10],[15,18]]intervals.sort(key=lambda x:(x[0],x[1]))# print(intervals)ans=[]start=intervals[0][0]end=intervals[0][1]for i in range(n):if end>=intervals[i][0]:if end<intervals[i][1]: #应对intervals=[[1,4],[2,3]]这种全包的情况end=intervals[i][1]if i==n-1:ans.append([start,end])else:ans.append([start,end])start=intervals[i][0]end=intervals[i][1]if i==n-1:ans.append([start,end])return ans

小技巧

1.这道题中明确告知S只包含小写字母 ‘a’ 到 ‘z’ ,这时可以考虑

    for ss in s: #预处理,先数每个字母有几个cnt[ord(ss)-ord('a')]+=1

2.移除列表中某个值

本题中的 tmp.remove(s[i]) #python中remove函数用于移除列表中某个值的第一个匹配项
tmp里面也仅有一个匹配项,所以可以效果是移除tmp里面的s[i]

但若有多个匹配项的情况

假设想删除emails中多个'something@something.com'
使用filter()和lambda将提供一种简洁的方法来删除不需要的值:

 newEmails = list(filter(lambda x : x != 'something@something.com', emails))
#这不会修改电子邮件。它创建一个新的列表newEmails,其中只包含匿名函数返回True的元素。

例十六:714. 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8

class Solution(object):def maxProfit(self, prices, fee):""":type prices: List[int]:type fee: int:rtype: int"""# prices=[1,4,3,8]result = 0minPrice = prices[0] #记录最低价格for i in range(1, len(prices)):if prices[i] < minPrice:minPrice = prices[i]elif prices[i] >= minPrice and prices[i] <= minPrice + fee: continueelse: #计算利润,可能有多次计算利润,最后一次计算利润才是真正意义的卖出result += prices[i] - minPrice - fee# print(result)minPrice = prices[i] - fee ##相当于在前一次prices[i]的基础上价格又上抬了,在这个prices[i]上更有的赚,因此在这个prices[i]上“卖”,前面花掉的手续费加回来#收获利润的这一天并不是收获利润区间里的最后一天(不是真正的卖出,相当于持有股票),所以后面要继续收获利润。#这两步操作一起,可以达到利润最大卖出时只扣一次手续费(result减了一次,minprice虽然也减了,但不算到最终结果当中来)# print(result)return result

实际应用题

发工资

链接:https://www.nowcoder.com/questionTerminal/e47cffeef25d43e3b16c11c9b28ac7e8
来源:牛客网

小度新聘请了一名员工牛牛, 每个月小度需要给牛牛至少发放m元工资(给牛牛发放的工资可以等于m元或者大于m元, 不能低于m)。
小度有一些钞票资金, 一共有n种不同的面额, 对于面额为x_i 的钞票, 小度有y_i张, 并且每一个钞票面额都能整除所有比它大的面额, 并且每一张钞票不能找零。
小度想知道这部分资金最多能牛牛发放多少个月的工资?

评论区答案:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=100010;
struct Money{int x, y;bool operator < (const Money e) const{return x<e.x;}
} money[N];
int main() {ios::sync_with_stdio(false);int n,m;cin>>n>>m;for(int i=0;i<n;++i)cin>>money[i].x>>money[i].y;sort(money,money+n);int ans=0;while(1){int need,rest=m;for(int i=n-1;i>=0;--i){if(money[i].y==0) continue;need=rest/money[i].x;if(need>money[i].y) need=money[i].y;money[i].y-=need;rest-=need*money[i].x;if(rest==0) break;}//不能凑齐m,优先用小额的钱去补if(rest!=0){for(int i=0;i<n;++i){if(money[i].y==0) continue;need=rest/money[i].x+1;if(need>money[i].y) need=money[i].y;money[i].y-=need;rest-=need*money[i].x;if(rest<=0) break;}}if(rest>0) break;ans++;}cout<<ans<<endl;return 0;
}

leetcode2021年度刷题分类型总结(八)贪心 (python)相关推荐

  1. leetcode2021年度刷题分类型总结(三)回溯法 (python)

    主要参考:代码随想录 分为三个部分对回溯法进行总结 1.组合 例一:77. 组合 组合问题关键在于[1,4]和[4,1]算作重复答案,也即取数时往前推进,取到4时不能回头取1,所以每次都要记录取到的位 ...

  2. leetcode2021年度刷题分类型总结(十)哈希表 (python)

    例一:202. 快乐数 编写一个算法来判断一个数 n 是不是快乐数. 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和. 然后重复这个过程直到这个数变为 1,也可能是 ...

  3. 2017-2018年度刷题记录

    2018暑假刷题1: T1.CF6A Triangle (#模拟 -1.4) T2.CF59A Word(#模拟 -1.5) T3.[洛谷]P2772 寻找平面上的极大点(#贪心 -1.4) T4.[ ...

  4. python刷题用leet_GitHub - Yolymaker/leetcode-python: 利用python分类刷leetcode题目

    leetcode分类高效刷题 leetcode是一个很好的学习算法的一个online judge的网站,通过刷题能够快速提升自己的算法能力.但是令大家都头疼的就是,怎么能够高效的通过leetcode刷 ...

  5. 二级python 刷题就能过吗_Python 刷题笔记:这很不python,官方大大能改下吗?

    惊讶:数值相等的整数或浮点数作为dict 字典的键将指向同一项?! 1.刷题遇到知识盲区 今天继续在 xue.cn 刷题,意外发现:整数和浮点数先后设为字典的键,会有奇怪的现象! 题目如下: What ...

  6. python刷题软件_教你用python写:HDU刷题神器

    声明:本文以学习为目的,请不要影响他人正常判题 HDU刷题神器,早已被前辈们做出来了,不过没有见过用python写的.大一的时候见识了学长写这个,当时还是一脸懵逼,只知道这玩意儿好屌-.时隔一年,决定 ...

  7. Python常用小技巧,提高刷题效率(适用于蓝桥杯python组)

    1. 掌握python标准库及小技巧 python课程学习到面向对象,就可以刷题参加算法比赛了 对于蓝桥杯不支持第三方库,但学会python标准库,将事半功倍: 2. 常用的列表函数 list1.ap ...

  8. 前端歌谣的刷题之路-第八十五题-列表动态渲染

    前言 我是歌谣 我有个兄弟 巅峰的时候排名c站总榜19 叫前端小歌谣 曾经我花了三年的时间创作了他 现在我要用五年的时间超越他 今天又是接近兄弟的一天人生难免坎坷 大不了从头再来 歌谣的意志是永恒的 ...

  9. 【Leetcode刷题记录_C++】【贪心】

    贪心 贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的. 分配问题 455. 分发饼干 假设你是一位很棒的家长,想要给你的孩子们一些小饼干.但是,每个孩子 ...

最新文章

  1. 找到一本不错的Linux电子书,附《Linux就该这么学》章节目录
  2. 微信小程序实现slideUp、slideDown滑动效果及点击空白隐藏功能示例
  3. 优酷背后的大数据秘密:资源弹性,可支撑EB级存储
  4. 启用属性,索引和存储的用途是什么?
  5. php header apk,php常用的header头
  6. 大家社区荣获最具影响力品牌
  7. ASP.NET身份验证机制membership入门——API篇
  8. bios开启虚拟化技术
  9. 编译原理与编译构造 LR文法
  10. Windows Mobile
  11. android ct扫描模拟,基于Android平台的CT图像可视化显示方法及实现
  12. Mathtype部分符号打不上去或部分符号点击后停止工作
  13. 交通锥,又称锥形路标、锥形筒、红帽子、方尖碑,是一种道路交通隔离警戒设施。
  14. 梦幻西游手游显示该服务器已满,梦幻西游手游100级突破任务怎么开启 解锁服务器100级任务攻略_《梦幻西游手游》官网...
  15. 知网论文 caj 转 pdf 方法【带目录且文字可选的pdf,非图片】
  16. 服务器制作raid,实战案例——服务器制作raid磁盘阵列并管理
  17. 基于SSH的计算机在线测评考试系统
  18. 手把手教你用Python分析豆瓣电影——以《我不是药神》《邪不压正》为例
  19. 递归分类最佳实践:如何在强化学习中用示例代替奖励
  20. 200G超强C语言和C++编程0基础从入门到精通视频教程,立即获取

热门文章

  1. DBNet++(TPAMI) 原理与代码解析
  2. 【VC++游戏开发#三】2D篇 —— 游戏之一:空中大战(SpaceWar)
  3. day13-event2
  4. 用笔记本电脑破解wifi密码
  5. python实现累乘multi函数(函数参数不限)
  6. 若依前后端分离框架学习-3:获取菜单
  7. HTML:基础语法,文档段落,修饰标签,特殊符号
  8. CA6140后托架(831001)加工工艺及夹具设计CAD图纸(论文 CAD图纸 开题报告 任务书 工艺卡 文献翻译)
  9. SZU形式语言与自动机理论期末复习
  10. ASP.NET收银管理系统源码(CS架构)