解题思路:
- 贪心算法:
没啥很复杂的,贪心算法的思路就是每次取得局部最优解,从而得到全局最优解,不过可能这个全局最优解并不是最优的。。
针对上面那道题,我们每次都把增长的取到,累加就可以得到最大利润。
public int maxProfit(int[] prices) {
int maxProfit = 0;
for (int i = 1; i < prices.length; i++) {
if (prices[i] > prices[i - 1]) {
maxProfit += prices[i] - prices[i - 1];
}
}
return maxProfit;
}
- 动态规划算法:
动态规划算法的思路是每次递推有两种选择,做还是不做,逐渐向下,好像一个展开的树,最终到达一个临界点获得最终的最大值后,逐层向上返回每层最大收益,最后得到全局最大收益;其和贪心算法的区别是贪心算法只关心局部的一个最优解,最终得到全局最优解,而这个全局最优解并不一定就真的是最优解,有点死心眼的感觉,而动态规划则纵观全局,得到最终全局最优解。
本题和常规动态规划有一些差异是他有两个递推公式,而有两个递归公式的原因是前一天可能买入,可能没买入,这就导致如果前一天买入了那今天只能选择卖出或不卖出,而不能买入,而如果前一天没买入,那今天就只能选择买入或者不买入。
下面来推导递推公式:首先我这里定义两个个参数:dp[i][0]作为当天不持有最大收益(利润)和dp[i][1]作为当天持有最大收益(利润),i是一共几天也就是题述prices的长度,我们明确,最后一天肯定是手里不持有股票收益最大,那么首先产生递推公式:
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
这里对公式做一下解释:我最后这天也就是今天想不持有,那第一种可能是昨天我就没持有,那我今天不买就是了,那也就是公式里的dp[i-1][0],第二种可能是昨天我持有股票,那我今天就得把它卖掉,否则我就损失了这prices[i]的利润了,所以就是公式里dp[i-1][1]+ prices[i]。
然后我们可以发现,如果这么递推,我缺少了对倒数第二天也就是dp[i-1][1]这个最大利润的支持,那我们就为这个dp[i-1][1]也做一个递推公式:
dp[i-1][1] = Math.max(dp[i - 2][1], dp[i - 2][0] - prices[i]);
这里再对这个公式进行一下解释:倒数第二天也就是昨天我想持有,那第一种可能是我前天就持有的,那我昨天就继续持有就行了,也就是公式里的dp[i-2][1],而第二种可能是我前天并没持有,那我昨天就得买进来,也就是公式里的dp[i-2][0]- prices[i],这就达成了我昨天持有股票的目的。
再说过来,我昨天可以持有,那其实我今天依旧可以持有,只不过那不是最大利润罢了,今天不理他就行了,毕竟我们要写代码的嘛,何必要管到底是今天还是昨天呢,把最大收益返回就是了,这就可以把上面这个递推公式调整为:
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
这就是两个递推公式的由来。
而这个递推公式的临界就是第一天,那就设置两个值:
dp[0][1] = -prices[0];//持有股票,刨掉成本
dp[0][0] = 0;//不持有股票,没成本
最终得到我们的动态规划解法(摘自LeetCode):
public int maxProfit(int[] prices) {
if (prices == null || prices.length < 2)
return 0;
int length = prices.length;
int[][] dp = new int[length][2];
//初始条件
dp[0][1] = -prices[0];
dp[0][0] = 0;
for (int i = 1; i < 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]);
}
//最后一天肯定是手里没有股票的时候,利润才会最大,
//只需要返回dp[length - 1][0]即可
return dp[length - 1][0];
}
以上均为本人自己理解,如有问题,欢迎大佬们留言~~