牛客算法心得——买卖股票的最好时机三(dp)

大家好,我是晴天学长, 一个找状态的经典题,需要的小伙伴可以关注支持一下哦!后续会继续更新的。


1) .买卖股票的最好时机(三)


假设你有一个数组prices,长度为n,其中prices[i]是某只股票在第i天的价格,请根据这个价格数组,返回买卖股票能获得的最大收益
1. 你最多可以对该股票有两笔交易操作,一笔交易代表着一次买入与一次卖出,但是再次购买前必须卖出之前的股票
2. 如果不能获取收益,请返回0
3. 假设买入卖出均无手续费

要求: 空间复杂度 O(n)O(n),时间复杂度 O(n)O(n)
进阶:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
输入描述:
第一行输入一个正整数 n ,表示数组 prices 的长度
第一行输入 n 个正整数,表示数组 prices 的所有元素的值
输出描述:
输出最大收益
示例1
输入:
6
8 9 3 5 1 3
输出:
4
复制
说明:
第三天(股票价格=3)买进,第四天(股票价格=5)卖出,收益为2
第五天(股票价格=1)买进,第六天(股票价格=3)卖出,收益为2
总收益为4.

示例2
输入:
4
9 8 4 1
输出:
0
示例3
输入:
5
1 2 8 3 8
输出:
12
说明:
第一笔股票交易在第一天买进,第三天卖出;第二笔股票交易在第四天买进,第五天卖出;总收益为12。
因最多只可以同时持有一只股票,所以不能在第一天进行第一笔股票交易的买进操作,又在第二天进行第二笔股票交易的买进操作(此时第一笔股票交易还没卖出),最后两笔股票交易同时在第三天卖出,也即以上操作不满足题目要求。



2) .算法思路

买卖股票的最好时机(三)
最大收益
dp[i][j] 为第i天股票买卖状态的最大收益
0.不操作
1.第一次买入buy1
2第一次卖出sale1
3第二次买入buy2
4第二次卖出sale2


3)算法步骤

1.读取输入值:

(1)读取第一行,包含一个整数 n,表示股票价格的天数。
(2)读取第二行,包含 n 个以空格分隔的整数,表示每天的股票价格。
2.创建一个大小为 n x 5 的二维数组 dp,用于存储动态规划的状态。每行代表一天,每列代表一个特定的状态:

(1)dp[i][0] 表示第 i 天没有进行任何交易时的最大利润。
(2)dp[i][1] 表示第 i 天进行第一次买入后的最大利润。
(3)dp[i][2] 表示第 i 天进行第一次卖出后的最大利润。
(4)dp[i][3] 表示第 i 天进行第二次买入后的最大利润。
(5)dp[i][4] 表示第 i 天进行第二次卖出后的最大利润。
3.初始化初始状态:

(1)将 dp[0][1] 设置为第一天股票价格的负值,表示在第 0 天买入。
(2)将 dp[0][3] 设置为第一天股票价格的负值,表示在第 0 天再次买入。
4.从第二天(i = 1)开始迭代到最后一天(n - 1):

(1)将 dp[i][0] 设置为 0,因为这一天没有进行任何交易。
(2)更新 dp[i][1],取 dp[i - 1][1] 和 dp[i - 1][0] - nums[i] 中的最大值,表示不买或在这一天买入。
(3)更新 dp[i][2],取 dp[i - 1][2] 和 dp[i - 1][1] + nums[i] 中的最大值,表示不卖或在这一天卖出。
(4)更新 dp[i][3],取 dp[i - 1][3] 和 dp[i - 1][2] - nums[i] 中的最大值,表示不买或在这一天再次买入。
(5)更新 dp[i][4],取 dp[i - 1][4] 和 dp[i - 1][3] + nums[i] 中的最大值,表示不卖或在这一天再次卖出。
(6)更新 max 变量,取 max 和 dp[i][4] 中的最大值。
5.检查最大利润 max 是否大于 0:

(1)如果是,打印 max 的值。
(2)否则,打印 0。


4). 代码实例



import java.io.*;

public class Main {
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
    static String[] lines;

    public static void main(String[] args) throws IOException {
        lines = in.readLine().split(" ");
        int n = Integer.parseInt(lines[0]);
        int[] nums = new int[n];
        lines = in.readLine().split(" ");
        for (int i = 0; i < n; i++) {
            nums[i] = Integer.parseInt(lines[i]);
        }
        int[][] dp = new int[n][5];
        dp[0][1] = -nums[0];
        //第二次买入也算进去,只不过把第一次买入当成0了
        dp[0][3] = -nums[0];
        int max = 0;
        for (int i = 1; i < n; i++) {
            dp[i][0] = 0;
            dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - nums[i]);
            dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + nums[i]);
            dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - nums[i]);
            dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + nums[i]);
            max = Math.max(max, dp[i][4]);
        }
        if (max > 0) {
            out.println(max);
        } else out.println(0);
        out.flush();
        out.close();
    }
}


5). 总结

  • 动规的状态选择很重要,当一个复杂的时候,选择另一个状态为切入点。

试题链接:

你可能感兴趣的:(算法,算法)