题目描述
思路解析
一句话答案:LeetCode 714 买卖股票含手续费是两状态 DP:每天维护持股、空仓两态,空仓=max(空仓,持股+价−fee) 卖出才扣一次费、持股=max(持股,空仓−价),答案取空仓。时间 O(n)、空间 O(1)。
反复买卖还要交手续费,什么时候动手反而亏钱
给数组 prices,prices[i] 是第 i 天股价,另有固定手续费 fee,卖出扣一笔。可反复买卖,但同时最多持一股,想再买得先卖,求最大利润。题面 prices=[1,4,2,7,3,2,5,1,6]、fee=2,答案 8。难在手续费:差价太小时赚的不够交费,动手反亏。
把每天买卖不动都枚举一遍,为什么撑不住
暴力是每天买、卖、不动三选一全枚举挑最高。可 n 天就是 3 的 n 次方量级,稍多就跑不完。决定明天怎么选的只有此刻手里有没有股,把它记下来、每种只算一次,指数枚举就压成一趟线性递推(拿前面算好的值推后面)。
持股、空仓两个状态,各自记的是账上多少钱
每天收盘只两种状态:持股或空仓。两个变量记这两态的最大现金——hold 记收盘持股时最多剩多少,cash 记收盘空仓时最多剩多少。hold 通常为负,买入先垫钱,第 0 天以 1 元买那股账上成 -1。开局 hold=-prices[0]、cash=0。
两态怎么互相转移,手续费为什么只在卖出扣一次
每态两条来路取更划算的。持股要么接着拿旧 hold,要么今天买——昨天空仓掏 prices[i],取 hold=max(hold, cash-prices[i])。
空仓要么接着空旧 cash,要么今天卖——昨天持股以 prices[i] 卖出、加回股价再扣 fee,取 cash=max(cash, hold+prices[i]-fee)。
手续费只在卖出扣一次:一趟买卖收一次费,买入只垫股价,卖出才结清,两头都扣就交两回。
拿题面 fee=2 的示例逐天填两行
开局第 0 天价 1,hold=-1、cash=0。第 1 天价 4:hold=max(-1,0-4)=-1,cash=max(0,-1+4-2)=1。第 2 天价 2:hold=max(-1,1-2)=-1,cash=max(1,-1+2-2)=1。第 3 天价 7:hold=max(-1,1-7)=-1,cash=max(1,-1+7-2)=4。第 4 天价 3:hold=max(-1,4-3)=1(4 即第 3 天的 cash),cash=max(4,1+3-2)=4。第 5 天价 2:hold=max(1,4-2)=2,cash=max(4,2+2-2)=4。第 6 天价 5:hold=max(2,4-5)=2,cash=max(4,2+5-2)=5。第 7 天价 1:hold=max(2,5-1)=4,cash=max(5,4+1-2)=5。第 8 天价 6:hold=max(4,5-6)=4,cash=max(5,4+6-2)=8。空仓 cash=8 即最大利润。
答案为什么取空仓、hold 初值又设成负数,复杂度多少
每天两次取大,一遍 O(n);只留 hold、cash 两变量滚动,空间 O(1)。
买入别扣费——fee 只在卖出减这一次,两头都扣等于一趟交两回。hold 初值也别写成 0:第 0 天持股账上已垫掉买入价,初值得是 -prices[0],写 0 相当于白得一股。最后返回 cash 别返回 hold,收盘还攥着股票的钱没落袋,永远不会更优。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住这「两态」,下面每天都在它们之间切换。
顶行是每天股价(固定)。下面两行:持股 / 空仓,待逐天填。
第 0 天开局:空仓就是 0;若买入,现金先垫掉股价,变成负数。
填入:持股 hold[0]=-1(买了 1 元那股),空仓 cash[0]=0。
第 1 天(股价 4):持股两条路——接着拿(-1) 或 今天买(-4);空仓两条路——接着空(0) 或 今天卖(1,卖出已扣手续费 2)。
取较大的:hold[1]=-1(持股没变(不如继续拿)),cash[1]=1(今天卖出更划算)。
第 2 天(股价 2):持股两条路——接着拿(-1) 或 今天买(-1);空仓两条路——接着空(1) 或 今天卖(-1,卖出已扣手续费 2)。
取较大的:hold[2]=-1(持股没变(不如继续拿)),cash[2]=1(空仓没变(这天卖了反而亏,不如不卖))。
第 3 天(股价 7):持股两条路——接着拿(-1) 或 今天买(-6);空仓两条路——接着空(1) 或 今天卖(4,卖出已扣手续费 2)。
取较大的:hold[3]=-1(持股没变(不如继续拿)),cash[3]=4(今天卖出更划算)。
第 4 天(股价 3):持股两条路——接着拿(-1) 或 今天买(1);空仓两条路——接着空(4) 或 今天卖(0,卖出已扣手续费 2)。
取较大的:hold[4]=1(今天买入更划算),cash[4]=4(空仓没变(这天卖了反而亏,不如不卖))。
第 5 天(股价 2):持股两条路——接着拿(1) 或 今天买(2);空仓两条路——接着空(4) 或 今天卖(1,卖出已扣手续费 2)。
取较大的:hold[5]=2(今天买入更划算),cash[5]=4(空仓没变(这天卖了反而亏,不如不卖))。
第 6 天(股价 5):持股两条路——接着拿(2) 或 今天买(-1);空仓两条路——接着空(4) 或 今天卖(5,卖出已扣手续费 2)。
取较大的:hold[6]=2(持股没变(不如继续拿)),cash[6]=5(今天卖出更划算)。
第 7 天(股价 1):持股两条路——接着拿(2) 或 今天买(4);空仓两条路——接着空(5) 或 今天卖(1,卖出已扣手续费 2)。
取较大的:hold[7]=4(今天买入更划算),cash[7]=5(空仓没变(这天卖了反而亏,不如不卖))。
第 8 天(股价 6):持股两条路——接着拿(4) 或 今天买(-1);空仓两条路——接着空(5) 或 今天卖(8,卖出已扣手续费 2)。
取较大的:hold[8]=4(持股没变(不如继续拿)),cash[8]=8(今天卖出更划算)。
最右的空仓 cash[8]=8 就是最大利润——手里别留股,全部落袋。
边界先想清:差价没盖过手续费就别动手。
两个高频追问。
参考代码
def maxProfit(prices, fee): hold, cash = -prices[0], 0 for p in prices[1:]: hold = max(hold, cash - p) cash = max(cash, hold + p - fee) return cash复杂度
- 时间:O(n),一遍线性递推
- 空间:O(1),只留 hold/cash 两个状态
易错点
面试追问把动画讲成自己的话
追问不含手续费的版本(LC122)怎么变?
追问为什么最后取 cash 而不是 hold?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。再去主线里挑下一道练手。
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题