题目分别如下:
三道题如下,其实仔细观察,不难发现,第一题和第二题思路几乎一模一样,就是换了个说法,换汤不换药
主要思路为:
【指针i固定,其对应的values[i]也是固定的,也就是说,只需要确定某个数值的指针位置,其对应的数值确定。观光问题比股票问题稍微复杂一点点,i<j:max(values[i]+values[j]+i-j),可以写为i<j:max(values[i]+i)+max(values[j]-j),看成确定的两数之和,分别更新确定两数最大值即可。需要注意的一点是,条件为:i<j,即先更新j对应的values[j]-j,再更新values[i]+i】
#观光
class Solution:
def maxScoreSightseeingPair(self, values: List[int]) -> int:
res = 0
premax = values[0] + 0 #初始值
for j in range(1, len(values)):
res = max(res, premax + values[j] - j) #判断能否刷新res
premax = max(premax, values[j] + j) #判断能否刷新pre_max, 得到更大的A[i] + i
return res
#股票
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n=len(prices)
ans=prices[0]
maxcount=0
for i in range(n):
maxcount=max(maxcount,prices[i]-ans)
ans=min(ans,prices[i])
#print(ans,maxcount)
return maxcount
股票II继续延续上述思路,但多了一点点更有趣的思路,即贪心算法求局部最优解 。图解版晚点更新:
#类似于之前的“跳跃游戏”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值
#股票进阶II
class Solution:
#法1:效率最高,速度最快
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
summax = 0
for i in range(1, n):
p = prices[i - 1]
maxp = 0
# p=min放第一步表示当天可以买卖,利润为0;放后面则表示当天不可买卖,只能减去前n天的价格
# 类似于“跳跃游戏?”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值
# 当某点达到局部最大值时:
if p < prices[i]:
# if prices[i-1]<prices[i]:
# p=prices[i-1]
# maxp=-inf
# 更新局部区域内的最小价格
p = min(p, prices[i])
# p=min(prices[i-1],prices[i])
# 更新局部区域内的最大价格差
maxp = max(maxp, prices[i] - p)
# oldp=prices[i]
# print(p,prices[i],maxp)
# if oldp>=prices[i]:
summax += maxp
# print(p, maxp, summax)
# print(summax)
# oldp=prices[i]
# print(oldp)
return summax
#法2:
def maxProfit(self, prices) -> int:
n = len(prices)
summax = 0
p = prices[0]
maxp = 0
for i in range(1, n):
# 类似于“跳跃游戏?”,在每个局部中找到局部最优解,局部的最大最小值,每次检索到局部最大值时停下,必为某局部的最大利润,然后继续进行下一次重复搜索,最后进行局部最大值的加和,即为整体最大值
# 当某点在局部最大值之前时(局部最大值时为prices[i-1]>=prices[i]):
if prices[i - 1] < prices[i]:
# 更新局部区域内的最小价格
# p=min放第一步表示当天可以买卖,利润为0;放后面则表示当天不可买卖,只能减去前n天的价格
p = min(p, prices[i])
# p=min(prices[i-1],prices[i])
# 更新局部区域内的最大价格差
maxp = max(maxp, prices[i] - p)
# else:
# 当达到局部最大值时prices[i-1]>=prices[i]时,重新开始下一局部区域的检索,并把前一局部最优解保存:
summax += maxp
# print(summax)
p = prices[i]
# print(summax,p)
maxp = 0
# print(summax)
# oldp=prices[i]
# print(oldp)
return summax
【#法2清晰版思路:】
def maxProfit(self, prices: List[int]) -> int:
#增加一个元素-inf,避免最后一组局部最优一直为递增,无法跳出循环更新summax
prices.append(-inf)
n = len(prices)
summax = 0
p = prices[0]
maxp = 0
for i in range(1, n):
#递增时不断更新最小值和最大值,直到局部检索到最大值-最小值时候跳出循环
if prices[i - 1] < prices[i]:
#p = min(p, prices[i])
maxp = max(maxp, prices[i] - p)
#递增时累加即可,后面肯定大于前面,8-2=8-6+6-2
#maxp+=prices[i]-prices[i-1]
#print(maxp)
#跳出循环后(即下一个局部最优解的开始小于前一个值),进行最大差值的累加
else:
summax += maxp
p = prices[i]
#print(summax)
maxp = 0
#summax=max(summax,maxp)
return summax
【法2清晰思路优化进阶版,代码极简,类似于法3贪心算法,但通过if判断大大提高了法3的运行效率】
def maxProfit(self, prices: List[int]) -> int:
#增加一个元素-inf,避免最后一组局部最优一直为递增,无法跳出循环更新summax
prices.append(-inf)
n = len(prices)
#summax = 0
#p = prices[0]
maxp = 0
for i in range(1, n):
#递增时不断更新最小值和最大值,直到局部检索到最大值-最小值时候跳出循环
if prices[i - 1] < prices[i]:
#maxp = max(maxp, prices[i] - p)
#递增时累加即可,后面肯定大于前面,8-2=8-6+6-2
maxp+=prices[i]-prices[i-1]
return maxp
#法3:思路最简单,但执行效率并非最高,贪心算法
def maxProfit(self, prices: List[int]) -> int:
n=len(prices)
for i in range(1,n):
#maxp=max(maxp,maxp+prices[i]-prices[i-1])
maxp+=max(0,prices[i]-prices[i-1])
return maxp
【法1-3都为贪心算法思路,法4为状态方程】
#法4
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
#两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
#dp=[0]*(n,2)
#dp=[[0 for col in range(2)]for row in range(n)]
#dp[0][1]=-prices[0]
own=-prices[0]
#dp[0][0]=0
noown=0
for i in range(1,n):
#dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
own=max(own,noown-prices[i])
#dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
noown=max(noown,own+prices[i])
#return dp[n-1][0]
return noown
法2清晰思路执行效率,每次只更新最大差值:
【法2清晰思路优化进阶版,代码极简,类似于法3贪心算法,但通过if判断大大提高了法3的运行效率】
法3数学解法:
zhengitla
股票II问题整体来说有两种思路:
(1)贪心算法,从前往后依次求解局部最优解
(2)寻找状态方程的通解,然后递归【这里引入了二维动态规划矩阵,原因是存在两个维度的变量:时间维度(指针)和当前状态维度(0或1,即未持有(卖出状态)或者持有(买入状态)),以后可以进一步进行状态变量扩展】
力扣https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n==1:
return 0
#两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
#dp=[0]*(n,2)
dp=[[0 for col in range(2)]for row in range(n)]
dp[0][1]=-prices[0]
#own=-prices[0]
dp[0][0]=0
dp[1][1]=max(dp[0][1],-prices[1])
dp[1][0]=max(dp[0][0],dp[0][1]+prices[1])
#noown=0
for i in range(2,n):
#持有[i][1]
#持有状态仅跟前一状态和前两状态有关,即买入时间的限制
dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i])
#dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
#own=max(own,noown-prices[i])
#不持有[i][0],什么时候都可以卖出,无限制
dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
#noown=max(noown,own+prices[i])
return dp[n-1][0]
#return noown
#优化内存,动态规划
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n==1:
return 0
#两个维度,第一维储存时间信息,第二维储存状态信息(持有或卖出)
#dp=[0]*(n,2)
#dp=[[0 for col in range(2)]for row in range(n)]
#dp[0][1]=-prices[0]
#own=-prices[0]
#dp[0][0]=0
#dp[1][0]=max(dp[0][0],dp[0][1]+prices[1])
#dp[1][1]=max(dp[0][1],-prices[1])
a1=0
#dp[1][0]
a2=max(0,-prices[0]+prices[1])
#dp[1][1]
b1=max(-prices[0],-prices[1])
#noown=0
for i in range(2,n):
b2=b1
#持有[i][1]
#持有状态仅跟前一状态和前两状态有关,即买入时间的限制
#dp[i][1]=max(dp[i-1][1],dp[i-2][0]-prices[i])
b1=max(b1,a1-prices[i])
#own=max(own,noown-prices[i])
#不持有[i][0],什么时候都可以卖出,无限制
#dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i])
a1=a2
a2=max(a2,b2+prices[i])
#noown=max(noown,own+prices[i])
#return dp[n-1][0]
return a2
#return noown
优化后的执行效率:
力扣https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
#法1:状态方程/动态规划求解
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
n=len(prices)
#dp=[[0 for i in range(2)] for j in range(n)]
#i=1
#dp[0][1]=-prices[0]
#dp[0][0]=0
a1=-prices[0]
b1=0
#print(dp)
for i in range(1,n):
a2=a1
#持有
#dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
a1=max(a1,b1-prices[i])
#未持有
#dp[i][0]=max(dp[i-1][0],dp[i-1][1]+prices[i]-fee)
b1=max(b1,a2+prices[i]-fee)
return b1
#法2:贪心算法(代码比较妙,尤其是第二个if后的"buy=prices[i]"可以说是避免了重复循环和多变量的点睛之笔)
def maxProfit(self, prices: List[int], fee: int) -> int:
#贪心算法,以卖出价来看(卖出后进行操作,买入先进行操作,因此以最后一个操作为准,依次进行局部最优求解)
#以当前拥有的profit来看:
#卖出:+pricesp[j]
#买入:-prices[i]
#手续费:-fee
#因此,每进行一笔交易,买入花费总价-prices[i]-fee
n=len(prices)
buy=prices[0]+fee
maxp=0
for i in range(1,n):
#如果当前购入价格小于初始购入价格,则更新新的购入价格(买更便宜的)
if prices[i]+fee<buy:
buy=prices[i]+fee
print(buy)
#如果卖出价大于之前最小买入价,那么进行交易,一定可以获得利润
#
if prices[i]>buy:
maxp+=prices[i]-buy
buy=prices[i]
#print(maxp)
return maxp
法1状态方程:
法2贪心算法: