Leetcode 股票交易

股票交易系列共有六道题目,难度递增。

1题

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1:
Input: [7, 1, 5, 3, 6, 4]
Output: 5

max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)

题目解析

这题作为easy题其实是有一点难度的,可能是为了后面的题目做铺垫hhh题目需要找到在n天中股票交易所能获得的最大利润。首先,题目隐藏了一个信息,如果input是一个递减序列,那么这n天中最大利润就为0。

了解到特殊情况后,开始分析问题,这道题目用动态规划做会比较简单,因此分离子问题。回想上课时提到分离subproblem的方法,最经典的一个就是序列的prefix作为子问题求解,求的应该是当前所能获得的最大利润和当前看到的最小值。

根据思路写出代码:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int profit[prices.size()];
        // initialize
        memset(profit, 0, sizeof(profit));
        int min = prices[0], ans = 0;
        for (int i = 1; i < prices.size(); i++) {
            if (prices[i] < min) 
                min = prices[i];
            else //DP求得当前利润
                profit[i] = prices[i] - min;
            //ans保存当前最大利润
            ans = max(profit[i], ans);
        }
        return ans;
    }
};

2题

在1题的基础上,题目的条件修改为,n天中交易次数不限,但交易时间不能重叠(即先卖出再买入)

题目解析

因为可以多次买卖,所以一旦找到比前一个数大的数就进行交易。

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int profit = 0;
        int min = 0;
        min = prices[0];
        for (int i = 1; i < prices.size(); i++) {
            if (prices[i] < min)
                min = prices[i];
            else  {
                profit += prices[i] - min;
                min = prices[i];
            }
        }
        return profit;
    }
};

3题

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)

题目解析

在每次卖出后,需要休市一天才能进行下一次买卖。这题在看了题目的discuss才明白解题的方向。该题一共有3个状态,每一天都可以在这三个状态之间变化。条件是

在卖出之前需要买入
在卖出之后休市一天

首先画出状态分析图。
这里写图片描述
从上面的状态转换图可以列出下列式子

s0[i] = max(s0[i - 1], s2[i - 1])
s1[i] = max(s0[i - 1] - prices[i], s1[i - 1])
s2[i] = s1[i - 1] + prices[i]

初始化定义

s0[0] = 0
s1[0] = -prices[0]
s2[0] = 0

最终结果应该为s0,s1,s2三者中的最大值

根据上面的思路写出源代码

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.empty()) return 0;
        int s0[prices.size() + 1], s1[prices.size() + 1], s2[prices.size() + 1];
        memset(s0, 0, sizeof(s0));
        memset(s1, 0, sizeof(s1));
        memset(s2, 0, sizeof(s2));
        s0[0] = s2[0] = 0;
        s1[0] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            s0[i] = max(s0[i - 1], s2[i - 1]);
            s1[i] = max(s0[i - 1] - prices[i], s1[i - 1]);
            s2[i] = s1[i - 1] + prices[i];
        }
        return max(s0[prices.size() - 1], max(s1[prices.size() - 1], s2[prices.size() - 1]));
    }
};

4题

该题目每一次交易的时候需要交非负的手续费。

题目分析

该题一共有两个状态,有三种交易手段:买入、卖出、无行动。和3题一样画出状态转移

s0[i] = max(s0[i - 1], s1[i - 1] + prices[i] - fee);
            s1[i] = max(s0[i - 1] - prices[i], s1[i - 1]);

初始化,s0[0] = 0,s1[0] = -prices[0]
根据上述思路写出源代码

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        if (prices.empty()) return 0;
        int s0[prices.size()], s1[prices.size()];
        memset(s0, 0, sizeof(s0));
        memset(s1, 0, sizeof(s1));
        s0[0] = 0;
        s1[0] = -prices[0];
        for (int i = 1; i < prices.size(); i++) {
            s0[i] = max(s0[i - 1], s1[i - 1] + prices[i] - fee);
            s1[i] = max(s0[i - 1] - prices[i], s1[i - 1]);
        }
        return max(s0[prices.size() - 1], s1[prices.size() - 1]);
    }
};

5 & 6题

第五题的条件是只允许用户最多进行两次交易,而第六题的条件是只允许用户最多进行k次交易。

题目分析

两道题的原理可以一起解释,而第5题是第6题的其中一种特殊情况。建立一个2维数组dp[i][j],第一维表示最多可以交易的次数,第二维表示在i天时所能获得的最大利益。

dp[k][i] = max(dp[k][i - 1], dp[k - 1][i] + prices[i] - prices[j]) 0<= j < prices.size()
 = max(dp[k][i - 1], prices[i] + tmpMax)
tmpMax[i] = max(tmpMax[i - 1], dp[k - 1][i] - prices[i])

初始化,

tmpMax[0] = dp[k - 1][0] - prices[0]

结果为dp[k][prices.size() - 1]
根据上述思路写出源代码,时间复杂度为 O(kn)

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() <= 1) return 0;
        int k = 2;
        int f[k + 1][prices.size()];
        memset(f, 0, sizeof(f));
        for (int kk = 1; kk <= k; kk++) {
            int tmpMax = f[kk - 1][0] - prices[0];
            for (int i = 1; i < prices.size(); i++) {
                f[kk][i] = max(f[kk][i - 1], prices[i] + tmpMax);
                tmpMax = max(tmpMax, f[kk - 1][i] - prices[i]);
            }            
        }
        return f[k][prices.size() - 1];
    }
};

将上述代码的k = 2删除后放到IV题中运行,会出现超时的错误。因为时间复杂度是 O(kn) ,当k很大时,会出现超时的情况。但是当k逐渐增大,大于prices.size()时,其实做多能做的交易次数也仅是 prices.size()/2 。因此可以做一个特殊处理:将k > prices.size() / 2的情况看作可以无限次交易,也即是2题的情况。

根据上述思路写出源代码。

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        if (prices.size() <= 1) return 0;
        if (k > prices.size() / 2) {
            int m = 0;
            for (int i = 1; i < prices.size(); i++) {
                m += max(prices[i] - prices[i - 1], 0);
            }nn n
            return m;
        }
        int f[k + 1][prices.size()];
        memset(f, 0, sizeof(f));
        for (int kk = 1; kk <= k; kk++) {
            int tmpMax = f[kk - 1][0] - prices[0];
            for (int i = 1; i < prices.size(); i++) {
                f[kk][i] = max(f[kk][i - 1], prices[i] + tmpMax);
                tmpMax = max(tmpMax, f[kk - 1][i] - prices[i]);
            }            
        }
        return f[k][prices.size() - 1];
    }
};

总结

股票系列题其实是有共同点的,但此次是一个个单独的完成,没有做好一个可以通用的数据结构。在这一系列题我们可以注意到动态规划中,不仅有两个状态,也可以产生一个多个状态之间的转移,对于这类型题可以建立多个动态规划的数组,将状态转移方程详细地写出来后再写源代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值