Best Time To Buy And Sell Stock 买卖股票最佳时机

关键字:leetcode,Best Time To Buy And Sell Stock,算法,algorithm,动态规划,dynamic programmingjava

leetcode 上关于Best Time to Buy and Sell Stock主题的题目有四个:算法

这四个题目难度依次递增。大体意思就是,给咱们一个 List<Integer> prices ,而后让咱们找到怎么买卖才能得到最大收益。其中第四个问题是寻求一个通解,在给定 prices和最大买卖次数k的状况下,求最大收益。数组

首先大体的解题方向是动态规划,这个应该不难想到。以后就是怎么找到状态,怎么列状态转移方程。考虑某一天的状况,能够有以下三种状态:ui

  • 当天买入
  • 当天卖出
  • 当天什么也没作

由于这个题目咱们要找到最大收益,因此,若是最后一天的状态是买入的话,那么其收益必定不是最大的,由于最后一天买入的话,就没有机会卖出了。那么,上面的三个状态能够减小到两个:code

  1. 当天卖出。
  2. 卖出,可是不在当天(即在前面的某一天)。

因此,咱们用 soldAtToday[k] 来表示当天卖出且卖出的时候交易了 k 手的时候的最大收益,soldNotAtToday[k] 表明在以前某天卖出,且当前交易手数为 k 的时候的最大收益。那么,状态转移方程就能够用以下伪代码描述:ip

soldAtToday[k] =  max(soldAtYesterday[k] + price, soldNotAtYesterday[k - 1] + price);
soldNotAtToday[k] = max(soldAtYesterday[k], soldNotAtYesterday[k]);

其中,k 是交易次数。须要注意的是soldAtToday[k] = max(soldAtYesterday[k] + price, soldNotAtYesterday[k - 1] + price);中第一个备选项是soldAtYesterday[k],而不是soldAtYesterday[k - 1],其含义就是,把本应该昨天卖的延长一天到今天卖,因此交易次数仍是 k 。leetcode

具体实现的时候,咱们发现 soldAtToday 和 soldNotAtToday 都是只依赖于 soldAtYesterday 和 soldNotAtYesterday,因此咱们能够利用两个长度为 k + 1 的数组来完成 today 和 yesterday 数据的存储。get

package BestTimeToBuyAndSellStock.VersionIV;

@SuppressWarnings("Duplicates")
class Solution {

    private int quickSolve(int[] prices) {
        int len = prices.length, profit = 0;
        for (int i = 1; i < len; i++) {
            // as long as there is a price gap, we gain a profit.
            if (prices[i] > prices[i - 1]) profit += prices[i] - prices[i - 1];
        }
        return profit;
    }

    public int maxProfit(int k, int[] prices) {
        if (prices == null || prices.length <= 1 || k <= 0) {
            return 0;
        }

        int len = prices.length;

        if (k >= len / 2) return quickSolve(prices);


        int today = 1;
        int yesterday = 0;
        int[][] soldAt = new int[2][k + 1];
        int[][] soldNotAt = new int[2][k + 1];

        soldAt[today][0] = 0;
        soldAt[yesterday][0] = 0;
        soldNotAt[today][0] = 0;
        soldNotAt[yesterday][0] = 0;

        for (int i = 0; i < k + 1; i++) {
            soldAt[yesterday][i] = 0;
            soldNotAt[yesterday][i] = 0;
        }

        for (int i = 1; i < prices.length; i++) {
            int price = prices[i] - prices[i - 1];
            for (int j = 1; j < k + 1; j++) {
                soldAt[today][j] = Math.max(
                        soldAt[yesterday][j] + price,
                        soldNotAt[yesterday][j - 1] + price
                );
                soldNotAt[today][j] = Math.max(
                        soldAt[yesterday][j],
                        soldNotAt[yesterday][j]
                );
            }
            int tmp = yesterday;
            yesterday = today;
            today = tmp;
        }

        return Math.max(soldAt[yesterday][k], soldNotAt[yesterday][k]);
    }
}

观察代码发现,开头多了一个 quickSolve 。这是由于,若是直接把上面咱们描述的算法实现出来,提交上去,会出现 TimeLimted 的问题。仔细分析了一下超时的用例,发现他们的特征是 k 很大。其实,当 k 大于 prices.length / 2 的时候,就至关于没有 k 的限制,即随便买卖了,由于不考虑当天买当天卖的状况,或者把当天买卖的收益虚拟的记为 0 。那么咱们的算法就退化成最简单的状况,因而使用一个 quickSolve 来解决便可。
这个题目也对咱们平常工做提供了启示:出现问题了,找到瓶颈,分析特征,而后突破。it

相关文章
相关标签/搜索