一、题目描述:
二、思路分析:
首先在拿到题,读完没有任何思路。先跟昨天的题对比一下,昨天的股票问题在一个数组内求一个区间的最大值,而本题是求在一个数组内不限定区间个数(区间不能交叉)的和的最大值。
首先能够明确一点的是,因为是求最大利润,加上数组并不是有序的,需要对每一种情况都要列举出来,就是穷举?
怎么穷举?循环,递归,这两种都不太适合。
思路一:利用“状态”进行穷举,具体到每一天,看看总共有几种可能的“状态”,在找出每个“状态”对应的“选择”,根据对应的“选择”更新“状态”。
具体到每一天交易完,有两个状态:有股票,没股票,通过 0 和 1 表示,然后通过 i 表示第几天;
继续思考,第 i 天的两种情况(持有股票和不持有股票),拆分来看:
dp[i][0]:表示第 i 天交易完手上没有股票的最大利润,它的情况基于上一天的情况,第 i - 1 交易完就没有股票dp[i-1][0],第 i - 1 天交易完有股票,今天卖掉,获得 prices[i] 的收益;
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1]:表示第 i 天交易完手上有股票的最大利润,第 i - 1 天交易完就没有股票dp[i-1][0],今天需要购入股票,花费 prices[i] 的钱,为dp[i-1][0] - prices[i];第 i - 1天交易完有股票,今天不交易的利润为 dp[i-1][1]
dp[i][1] = Math.max(dp[i-1][0]-prices[i], dp[i-1][1])
然后通过循环计算出每一天的两个状态的最大利润。
考虑边缘情况,第一天的时候,没有前一天的依赖。可以特殊书写。
最后一点,在全部交易结束后,最后一天把股票卖出去的收益一定大于最后一天有股票不卖的钱。所以最后一天的值为 dp[n-1][0];
// Your runtime beats 96.73 % of javascript submissions
// Your memory usage beats 15.79 % of javascript submissions (39.9 MB)
// leetcode 第二次提交
// Your runtime beats 63.6 % of javascript submissions
// Your memory usage beats 10.87 % of javascript submissions (40.2 MB)
var maxProfit = function (prices) {
// length个元素,每个元素两个状态;
const dp = new Array(prices.length).fill(new Array(2).fill(0));
dp[0][0] = 0;
dp[0][1] = 0 - prices[0];
for (let i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[prices.length - 1][0];
};
时间复杂度:O(n),一次循环,每次循环中复杂度 O(1)
空间复杂度:O(n),开辟出O(2n)个空间存储规划中的所有状态,O(n) = O(2n)
优化,其实我们求最终的利润的时候,本次状态只跟上次状态相关,那么我们是不是可以通过变量变换优化掉空间复杂度,只存储上一次的两个状态值,分别使用 dp0 代表上一次的 dp[i-1][0],使用 dp1 代表上一次的 dp[i-1][1]
// Your runtime beats 80.32 % of javascript submissions
// Your memory usage beats 49.69 % of javascript submissions (39.5 MB)
var maxProfit = function (prices) {
dp0 = 0;
dp1 = 0 - prices[0];
for (let i = 1; i < prices.length; i++) {
dp0 = Math.max(dp0, dp1 + prices[i]);
dp1 = Math.max(dp1, dp0 - prices[i]);
}
return dp0;
};
优化完:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
思路二:从评论学习到的,不限制交易次数,只要收集到所有的上坡合集,一定是利益最大化的。今天比昨天涨了,卖出;今天比昨天跌了,不卖,拆成了一节一节的利润,合并到一起。
// Your runtime beats 80.32 % of javascript submissions
// Your memory usage beats 89.42 % of javascript submissions (39.2 MB)
var maxProfit = function (arr) {
if (arr == null || arr.length <= 1) return 0;
let profit = 0;
for (let i = 1; i < arr.length; i++) {
if (arr[i] > arr[i - 1]) { // 卖出有利可图
profit += (arr[i] - arr[i - 1]);
}
}
return profit;
}
三、总结:
本题对于自己来说是一道新题,代码不难,关键是能想到这个思路比较难,在刚看这道题都蒙了,不知道怎么去找多个区间的和最大,换一种思考方式,通过穷举状态来思考,找当天状态的最大值,需要列举当天的状态的情况,当天状态值的跟之前的相关,之前再跟之前的相关,可以穷举出所有情况,通过比较获得最大值。本题还有一种贪心算法,因为之前也没有接触过,等本次刷完题回顾的时候对每道题在进行一个扩展优化。加油!!!