关于买卖股票的递归DP算法的算法和基本结构是什么?

时间:2019-08-02 01:15:48

标签: c algorithm dynamic-programming

我花了大约3个小时来解决这个问题,但是我无法理解两行代码:

b[j]     = _max(b[j],     s[j] - prices[i]);
s[j + 1] = _max(s[j + 1], b[j] + prices[i]);

以下代码是DP解决方案的问题是: 买卖股票的最佳时间

假设您有一个数组,第i个元素是第i天给定股票的价格。

设计算法以找到最大利润。您最多可以完成k笔交易。

注意: 您可能不能同时进行多项交易(例如,必须先出售股票才能再次购买)。

示例1:

输入:[2,4,1],k = 2

输出:2

说明:在第1天买入(价格= 2)并在第2天卖出(价格= 4),利润= 4-2 = 2。

示例2:

输入:[3,2,6,5,0,3],k = 2

输出:7

说明:在第2天购买(价格= 2)并在第3天出售(价格= 6),利润= 6-2 = 4。 然后在第5天买入(价格= 0)并在第6天卖出(价格= 3),利润= 3-0 = 3。

int _max(int a, int b) {
    return a > b ? a : b;
}
int all_profits(int* prices, int pricesSize) {
    int i, d, p;

    p = 0;
    for (i = 1; i < pricesSize; i ++) {
        d = prices[i] - prices[i - 1];
        p = d > 0 ? p + d : p;  // get it as long as it is a profit!
    }

    return p;
}
int maxProfit(int k, int* prices, int pricesSize) {
    int *b, *s, *buff, i, j, p;

    if (pricesSize < 2) return 0;

    if (k >= pricesSize / 2) return all_profits(prices, pricesSize);

    buff = malloc((2 * k + 1) * sizeof(int));
    //assert(buff);
    b = &buff[0];
    s = &buff[k];

    for (i = 0; i < k; i ++) {
        b[i] = 0x80000000;  // min integer
        s[i] = 0;
    }
    s[k] = 0;

    for (i = 0; i < pricesSize; i ++) {
        for (j = 0; j < k; j ++) {
            // profit on buy is current buy or last sale minus today's price
            b[j]     = _max(b[j],     s[j] - prices[i]);
            // profit on sale is current sale or last buy plus today's price
            s[j + 1] = _max(s[j + 1], b[j] + prices[i]);
        }
    }

    p = s[k];

    free(buff);

    return p;
}

除了开头提到的两行内容,我了解所有代码:

  • buff数组的用途是什么? buff数组分为两部分,一个包含b,另一个包含s。据我了解,s [n]是您可以在第n天获得的最大利润。我不知道b在做什么。
  • 为什么我们要执行b[j] = _max(b[j], s[j] - prices[i]);的MAX,所以购买价格不应该是最低的吗?为什么b [j]这两个事物的最大值? s [j]-价格[i]是什么?
  • 这可能也与算法有关,但是为什么要这样声明:s[j + 1] = _max(s[j + 1], b[j] + prices[i]);在此表达式中b [j] + price [i]是什么意思?
  • 为什么我们每天要经历k次:for (j = 0; j < k; j ++) {

我花了很多时间(3个小时)考虑此解决方案并将其与其他DP问题进行比较,但是没有运气。我将它与最长增长子序列DP问题进行了比较,以及如何“让length(k)表示终止于位置k的最长增长子序列的长度”,并尝试将该逻辑应用于此处的数组,但是没有运气。

谢谢您的帮助。

1 个答案:

答案 0 :(得分:2)

请考虑我们希望将每个价格(或一天)视为购买日或销售日,以得出最佳选择。 b列表将每天视为buy天,并将s列表视为sell天。现在我们只是实现逻辑。可能有点令人困惑的是,由于s是在j + 1更新的,对于s列表,j是前一天。

价格为k的最佳第i个购买日是我们已经拥有的第k个购买日或我们购买的等于(k-1)的购买日自我们刚买进以来,第一个卖出天数(即令人困惑的s[j])减去买入价!

b[j] = _max(b[j], s[j] - prices[i]);

价格为k的第i个最佳买入日是我们第k个销售日所拥有的,或者是第k个买入日最好的(因为第k条交易同时有买和卖)加上今天的价格,因为我们只是通过卖出股票而赚了!

s[j + 1] = _max(s[j + 1], b[j] + prices[i]);

更新

根据OP的请求,下面是一个示例:[5, 20, 15, 100, 35] k = 2

b represents the most profit at
the jth buy considering prices up to i:
max(b[j], s[j] - prices[i])

s represents the most profit at
the jth sell (index j+1 in s) considering prices up to i:
max(s[j + 1], b[j] + prices[i])

note that when j = 0, s[j] (the sell before the first buy)
is always 0

prices[0]:
  j:  0     1
  b:  -5    -5 // max(-Inf, 0 - 5), max(-Inf, 0 - 5)
  s:  0     0  // max(0, -5 + 5), max(0, -5 + 5)

prices[1]:
  j:  0     1
  b:  -5    -5 // max(-5, 0 - 20), max(-5, 15 - 20)
  s:  15    15 // max(0, -5 + 20), max(0, -5 + 20)

prices[2]:
  j:  0    1
  b:  -5   0   // max(-5, 0 - 15), max(-5, 15 - 15)
  s:  15   15  // max(15, -5 + 15), max(15, 0 + 15)    

prices[3]:
  j:  0    1
  b:  -5   0   // max(-5, 0 - 100), max(0, 0 - 100) 
  s:  95   100 // max(15, -5 + 100), max(15, 0 + 100)

prices[4]:
  j:  0    1
  b:  -5   60  // max(-5, 0 - 35), max(0, 95 - 35)
  s:  95   100 // max(95, -5 + 35), max(100, 60 + 35)