188. 买卖股票的最佳时机 IV(Java)(动归正向迭代)

1 题目

给定一个数组,它的第 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 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

2 Java

2.1 方法一(动归正向迭代)

class Solution {
    /*
        dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
              max(   选择 rest  ,           选择 sell      )
        今天没持股,两种可能,1今天没买,2今天刚卖
        dp[i-1][k][0] 到 dp[i][k][0]:昨天没持股,今天也没持股(今天没买)
        dp[i-1][k][1] 到 dp[i][k][0]:昨天持股了,今天没持股(今天刚卖)

        dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
                    max(   选择 rest  ,           选择 buy         )
        今天持股,两种可能,1今天没卖,2今天刚买
        dp[i-1][k][1] 到 dp[i][k][1]:昨天持股,今天也持股(今天没卖)
        dp[i-1][k-1][0] 到 dp[i][k][1]:昨天没持股,今天持股(今天刚买)

        求dp[I][?][0],即最后一天,完成了?次交易,目前不持股,的收益,即为最大收益

        买的当天,k会+1,持有变1
        卖的当天,k不变,持有变0

        本题k未知,作为参数传入,最多交易k次
    */

    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0 || k == 0)  return 0;
        int I = prices.length, K = k + 1, J = 2;

        // 若 k 相当于无限制,相当于题目股票II,直接贪心
        if(2 * (K - 1) >= I)    return maxProfitInfinit(prices);

        // 若限制 k 次交易
        return maxProfitLimit(k, prices);
    }

    // 若不限制交易次数(股票II贪心解法)
    public int maxProfitInfinit(int[] prices) {
        int profit = 0;
        for(int i = 1; i < prices.length; i++){
            // 今天比昨天高,就算利润
            profit += Math.max(prices[i] - prices[i - 1], 0);
        }
        return profit;
    }

    // 若限制 k 次交易
    public int maxProfitLimit(int kLimit, int[] prices){
        int I = prices.length, K = kLimit + 1, J = 2;

        // 创建初始化备忘录
        int[][][] dp = new int[I][K][J];
        for(int i = 0; i < Math.min(2 * (K - 1), I); i++){  // 暴力点就直接全部初始化-99999了
            dp[i][0][0] = 0;            // 注意
            dp[i][0][1] = - 99999;
            for(int k = 1; k < K; k++){
                dp[i][k][0] = - 99999;
                dp[i][k][1] = - 99999;
            }
        }
        dp[0][1][1] = - prices[0];      // 注意

        // 外层for状态步进
        for(int i = 1; i < I; i++){
            for(int k = 1; k < K; k++){	// k = 0的状态值都是0,一般不可能是解(没交易),没有计算的必要
                // 内存for状态转移
                dp[i][k][0] = Math.max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
                dp[i][k][1] = Math.max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
            }
        }

        // 选最大值,有可能交易1次比交易2次赚的多,并不是交易次数越多越好
        int ans = 0;
        for(int k = 1; k < K; k++)  ans = Math.max(ans, dp[I - 1][k][0]);
        return ans;
    }
}

2.2 方法二()


发布了131 篇原创文章 · 获赞 0 · 访问量 2254

猜你喜欢

转载自blog.csdn.net/weixin_43969686/article/details/105600772
今日推荐