[Lintcode] Best Time to Buy and Sell Stock IV

35 篇文章 0 订阅
24 篇文章 0 订阅

问题描述

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 at most k transactions.

Example
Given prices = [4,4,6,1,1,4,2,5], and k = 2, return 6.
简而言之,就是从prices数组中,取出最多2*k个数,相邻的每对(比如第1个数和第2个数,第3个数和第4个数),其总和要最大。

Note
You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

题意分析

这是一个DP (Dynamic Programming)问题。

解法1

t(n, k):表示前n天,进行最多k次交易,所能获得的最大收益。并且最后一次交易发生在第n天
那么有:
t(n, k) = max { t(m, k-1) + prices[n] - prices_min(m+1, n) }, 0 <= m <= n-1
t(n, k) = max { t(n, k), t(n, k-1) } // 只最多进行 k-1 次交易
其中 prices_min(m+1, n) 表示第m+1天到第n天之间的最低股票价格

这样的话,可以对m从n-1变换到0,进行扫描一次,并且更新prices_min,这样的话,要求得到t(n, k) 的复杂度为 o(n),因此总的时间复杂度为 O(k * n^2)

解法2

解法一的复杂度还是有点大,当k趋近于n的时候,复杂度可以达到 O(n^3),不太可以接受。因此我们需要一个更好的dp方法。

g(n, k)表示前n天,进行k次交易,所获得最大收益
l(n, k) 表示前n天,进行k次交易,且最后一天进行了交易(卖出股票),所获得最大收益

g(n, k) = max{ g(n-1, k), l(n, k) } // 前n天最大收益,要不最后一次交易在前n-1天内,要不发生在第n天
l(n, k) = max{ g(m, k-1) + prices[n] - prices[m+1] }, 0 <= m <= n-1
l(n, k) = max{ l(n, k), l(n, k-1) }

在计算 l(n, k) 的时候,需要遍历 0 ~ n-1,如果暴力的话,复杂度还是 O(n),最后的复杂度就是O(k * n^2)

那么这里有个优化算法:
设max_diff 为 max { g(m, k-1) - prices[m+1] }, 0 <= m <= n-1。在计算 l(0, k) ~ l(n, k) 的时候,不断更新max_diff,那么这样的话,其实就把计算 l(n,k) 的平摊复杂度降了下来,复杂度为 O(1)。那么总的时间复杂度为 O(kn)。

具体代码:

    int maxProfit(int k, vector<int> &prices) {
        // write your code here
        int n = prices.size();
        if (k > n/2) k = n/2;
        if (n <= 1) return 0;
        int *l = new int[n+1];
        int *g = new int[n+1];

        for (int i = 1; i <= k; i++) {
            if (i == 1) {
                int current_min = prices[0], max_profit = 0;
                for (int j = 0; j <= n; j++) {
                    if (j == 0) { l[j] = g[j] = 0; continue; }
                    current_min = min(current_min, prices[j-1]);
                    max_profit = max(max_profit, prices[j-1]- current_min);
                    l[j] = prices[j-1] - current_min;
                    g[j] = max_profit;
                }
//              print(l, n+1); print(g, n+1); printf("---\n");
            } else {
                int max_diff = g[0] - prices[0];
                l[0] = g[0] = 0;
                for (int j = 1; j <= n; j++) {
                    max_diff = max(max_diff, g[j-1] - prices[j-1]);
//                  printf("j:%d, max_diff:%d\n ", j, max_diff);
                    l[j] = max(l[j], max_diff + prices[j-1]);
                }
//              cout << endl;
                // update g
                for (int j = 1; j <= n; j++) {
                    g[j] = max(g[j-1], l[j]);
                }
//              print(l, n+1); print(g, n+1); printf("---\n");
            }
        }
        return g[n];
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值