动态规划,买股票系列

买股票系列

有限状态机。一种区别与序列dp定义的动态规划定义方法。

通用框架
int n = prices.length, K = k;
int memo[][][] = new int[n + 1][K + 1][2];
for (int i = 0; i <= n; i++) memo[i][0][1] = Integer.MIN_VALUE;
for (int i = 0; i <= K; i++) memo[0][i][1] = Integer.MIN_VALUE;

for (int i = 1; i <= n; i++) {
    for ( k = 1; k <= K; k++) {
        memo[i][k][0] = Math.max(memo[i - 1][k][0], memo[i - 1][k][1] + prices[i-1]);
        memo[i][k][1] = Math.max(memo[i - 1][k][1], memo[i - 1][k - 1][0] - prices[i-1]);
    }
}

状态压缩

int n = prices.length;
int memo[][] = new int[k + 1][2];
for (int i = 0; i <= k; i++) memo[i][1] = Integer.MIN_VALUE;

for (int i = 1; i <= n; i++) {
    for (int j = 1; j <= k; j++) {
        memo[j][0] = Math.max(memo[j][0], memo[j][1] + prices[i-1]);
        memo[j][1] = Math.max(memo[j][1], memo[j - 1][0] - prices[i-1]);
    }
}
问题
Best Time to Buy and Sell Stock
/*
* 121. Best Time to Buy and Sell Stock
* 买股票一,一次
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
*
* */

解法一:one pass

/*
原理,高个子排队,one pass
Runtime: 1 ms, faster than 98.84% of Java online submissions for Best Time to Buy and Sell Stock.
Memory Usage: 52 MB, less than 47.42% of Java online submissions for Best Time to Buy and Sell Stock.
* */
class Solution {
    public int maxProfit(int[] prices) {
        int res, max;
        res = max = 0;
        for (int i = prices.length - 1; i >= 0; --i) {
            if (max > prices[i]) res = Math.max(res, max - prices[i]);
            else max = prices[i];
        }
        return res;
    }
}

解法二:动态规划

  • dp(n)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润
    d p ( n ) [ 0 ] = m a x ( d p ( n − 1 ) [ 0 ] , d p ( n − 1 ) [ 1 ] + p r i c e s [ n − 1 ] ) d p ( n ) [ 1 ] = m a x ( d p ( n − 1 ) [ 1 ] , − p r i c e s [ n − 1 ] ) dp(n)[0] = max(dp(n-1)[0], dp(n-1)[1] + prices[n-1])\\ dp(n)[1] = max(dp(n-1)[1], - prices[n-1]) dp(n)[0]=max(dp(n1)[0],dp(n1)[1]+prices[n1])dp(n)[1]=max(dp(n1)[1],prices[n1])
  • 状态n
  • 选择,当前两个状态从何处发展而来
  • base,基值情况比较复杂,当n == 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。
class SolutionV2 {
    private static int[][] memo;

    public int maxProfit(int[] prices) {
        int n = prices.length;
        memo = new int[n+1][2];
        return dp(n, prices)[0];
    }

    private int[] dp(int n, int[] prices) {
        if (n == 0) {
            memo[n][1] = Integer.MIN_VALUE;
            return memo[n];
        }
        if(memo[n][0]!=0) return memo[n];
        memo[n][0] = Math.max(dp(n - 1, prices)[0], dp(n - 1, prices)[1] + prices[n - 1]);
        memo[n][1] = Math.max(dp(n - 1, prices)[1],  - prices[n - 1]);
        return memo[n];
    }

    public static void main(String[] args) {
        int[] input = {7, 1, 5, 3, 6, 4};
        System.out.println(new SolutionV2().maxProfit(input));
        ArrayTools.disp2DArray(memo);

    }
}

解法二改进,状态压缩

/*

- dp(n)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润
    $$
    dp(n)[0] = max(dp(n-1)[0], dp(n-1)[1] + prices[n-1])//
    dp(n)[1] = max(dp(n-1)[1], - prices[n-1])
    $$
- 状态n
- 选择,当前两个状态从何处发展而来
- base,基值情况比较复杂,当n == 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。
Runtime: 2 ms, faster than 50.63% of Java online submissions for Best Time to Buy and Sell Stock.
Memory Usage: 52 MB, less than 42.96% of Java online submissions for Best Time to Buy and Sell Stock.
* */
class SolutionV2_1 {
    private static int[][] memo;

    public int maxProfit(int[] prices) {
        int n = prices.length, state0 = 0, state1 = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            state0 = Math.max(state0, state1 + prices[i-1]);
            state1 = Math.max(state1, -prices[i - 1]);
        }
        return state0;
    }

}
Best Time to Buy and Sell Stock II
* 122. Best Time to Buy and Sell Stock II
* 股票问题二,多次买卖
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/
*

解法一:贪心

one pass, 降价之前卖出,降价的瞬间发生重新购入,
这个直觉是正确的,假设
p [ i ] < p [ i − 1 ] < p [ i + k ] p[i] < p[i-1] < p[i + k] p[i]<p[i1]<p[i+k]
此时,以p[i-1]作为最高价格卖出,然后以p[i]购入。
因为
p [ i + k ] − p r e < ( p [ i + k ] − p [ i ] ) + ( p [ i − 1 ] − p r e ) = ( p [ i + k ] − p r e ) + ( p [ i − 1 ] − p [ i ] ) p[i + k] - pre < (p[i+k] - p[i]) + (p[i-1] - pre) \\ = (p[i+k] - pre) + (p[i-1] - p[i]) p[i+k]pre<(p[i+k]p[i])+(p[i1]pre)=(p[i+k]pre)+(p[i1]p[i])

/*
one pass, 降价之前卖出,降价的瞬间发生重新购入,
这个直觉是正确的,假设
$$
p[i] < p[i-1] < p[i + k]
$$
此时,以p[i-1]作为最高价格卖出,然后以p[i]购入。
因为
$$
p[i + k] - pre < (p[i+k] - p[i]) + (p[i-1] - pre) //
= (p[i+k] - pre) + (p[i-1] - p[i])
$$
Runtime: 1 ms, faster than 71.52% of Java online submissions for Best Time to Buy and Sell Stock II.
        Memory Usage: 38.8 MB, less than 43.47% of Java online submissions for Best Time to Buy and Sell Stock II.
*/

class Solution {
    public int maxProfit(int[] prices) {
        int n, prof, curProf, curCost;
        n = prices.length; prof = curProf = 0; curCost = prices[0];
        for (int i = 1; i < n; i++) {
            if (prices[i] > prices[i - 1]) {
                curProf = prices[i] - curCost;
            } else {
                curCost = prices[i];
                prof += curProf;
                curProf = 0;
            }
        }
        return prof += curProf;
    }
}

解法二动态规划

  • dp(n)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润
    d p ( n ) [ 0 ] = m a x ( d p ( n − 1 ) [ 0 ] , d p ( n − 1 ) [ 1 ] + p r i c e s [ n − 1 ] ) d p ( n ) [ 1 ] = m a x ( d p ( n − 1 ) [ 1 ] , d p ( n − 1 ) [ 0 ] − p r i c e s [ n − 1 ] ) dp(n)[0] = max(dp(n-1)[0], dp(n-1)[1] + prices[n-1])\\ dp(n)[1] = max(dp(n-1)[1], dp(n-1)[0] - prices[n-1]) dp(n)[0]=max(dp(n1)[0],dp(n1)[1]+prices[n1])dp(n)[1]=max(dp(n1)[1],dp(n1)[0]prices[n1])
  • 状态n
  • 选择,当前两个状态从何处发展而来
  • base,基值情况比较复杂,当n == 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。
/*
Runtime: 1 ms, faster than 71.52% of Java online submissions for Best Time to Buy and Sell Stock II.
Memory Usage: 39 MB, less than 27.09% of Java online submissions for Best Time to Buy and Sell Stock II.
* */
class SolutionV2 {
    private static int[][] memo;

    public int maxProfit(int[] prices) {
        int n = prices.length, state0 = 0, state1 = Integer.MIN_VALUE,t;
        for (int i = 1; i <= n; i++) {
            t = state0;
            state0 = Math.max(state0, state1 + prices[i-1]);
            state1 = Math.max(state1, t - prices[i - 1]);
        }
        return state0;
    }
Best Time to Buy and Sell Stock III
/*
* 123. Best Time to Buy and Sell Stock III
* 买股票三,买三次
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
*
* */
  • dp(n, k)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润, n代表天数,k代表可交易的次数
    d p ( n , k ) [ 0 ] = m a x ( d p ( n − 1 , k ) [ 0 ] , d p ( n − 1 , k ) [ 1 ] + p r i c e s [ n − 1 ] ) d p ( n , k ) [ 1 ] = m a x ( d p ( n − 1 , k ) [ 1 ] , d p ( n − 1 , k − 1 ) [ 0 ] − p r i c e s [ n − 1 ] ) dp(n, k)[0] = max(dp(n-1, k)[0], dp(n-1, k)[1] + prices[n-1])\\ dp(n, k)[1] = max(dp(n-1, k)[1], dp(n-1, k-1)[0] - prices[n-1]) dp(n,k)[0]=max(dp(n1,k)[0],dp(n1,k)[1]+prices[n1])dp(n,k)[1]=max(dp(n1,k)[1],dp(n1,k1)[0]prices[n1])

  • 状态n

  • 选择,当前两个状态从何处发展而来

  • base,基值情况比较复杂,当n == 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。

解法一:动态规划,迭代

/*
Time Limit Exceeded

* */
class SolutionV1 {
    private static int[][][] memo;

    public int maxProfit(int[] prices) {
        int n = prices.length;
        memo = new int[n+1][2+1][2];
        return dp(n, 2, prices)[0];
    }

    private int[] dp(int n, int k, int[] prices) {
        if (n == 0 || k == 0) {
            memo[n][k][1] = Integer.MIN_VALUE;
            return memo[n][k];
        }
        if(memo[n][k][0]!=0) return memo[n][k];
        memo[n][k][0] = Math.max(dp(n - 1, k, prices)[0], dp(n - 1, k, prices)[1] + prices[n - 1]);
        memo[n][k][1] = Math.max(dp(n - 1, k, prices)[1],  dp(n - 1, k - 1, prices)[0] - prices[n - 1]);
        return memo[n][k];
    }
}

解法一改进,迭代

/*
迭代解法
Runtime: 96 ms, faster than 5.40% of Java online submissions for Best Time to Buy and Sell Stock III.
Memory Usage: 65.9 MB, less than 22.71% of Java online submissions for Best Time to Buy and Sell Stock III.
* */
public class SolutionV1_1 {

    public int maxProfit(int[] prices) {
        int n = prices.length, K = 2;
        int memo[][][] = new int[n + 1][K + 1][2];
        for (int i = 0; i <= n; i++) memo[i][0][1] = Integer.MIN_VALUE;
        for (int i = 0; i <= K; i++) memo[0][i][1] = Integer.MIN_VALUE;

        for (int i = 1; i <= n; i++) {
            for (int k = 1; k <= K; k++) {
                memo[i][k][0] = Math.max(memo[i - 1][k][0], memo[i - 1][k][1] + prices[i-1]);
                memo[i][k][1] = Math.max(memo[i - 1][k][1], memo[i - 1][k - 1][0] - prices[i-1]);
            }
        }
        return memo[n][K][0];
    }
}

解法一改进,状态压缩

/*
迭代解法, 状态压缩
Runtime: 4 ms, faster than 67.78% of Java online submissions for Best Time to Buy and Sell Stock III.
Memory Usage: 55.5 MB, less than 36.15% of Java online submissions for Best Time to Buy and Sell Stock III.
* */
public class SolutionV1_2 {
    public int maxProfit(int[] prices) {
        int n, K, dp_i_1_0, dp_i_1_1, dp_i_2_0, dp_i_2_1, dp_ip_0_0, dp_ip_1_0;
        n = prices.length; K = 2; dp_i_1_0 = dp_i_2_0 = dp_ip_0_0 = dp_ip_1_0 = 0;dp_i_1_1 = dp_i_2_1 = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            dp_i_1_0 = Math.max(dp_i_1_0,dp_i_1_1 + prices[i-1]);
            dp_i_1_1 = Math.max(dp_i_1_1, dp_ip_0_0 - prices[i-1]);
            dp_i_2_0 = Math.max(dp_i_2_0, dp_i_2_1 + prices[i-1]);
            dp_i_2_1 = Math.max(dp_i_2_1, dp_ip_1_0 - prices[i-1]);
            dp_ip_1_0 = dp_i_1_0;
        }
        return dp_i_2_0;
    }
}
Best Time to Buy and Sell Stock IV
/*
* 188. Best Time to Buy and Sell Stock IV
* 购买股票四,k次购买
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/
*
* */

解法一:动态规划迭代

/*

Runtime: 7 ms, faster than 35.77% of Java online submissions for Best Time to Buy and Sell Stock IV.
        Memory Usage: 41.8 MB, less than 11.50% of Java online submissions for Best Time to Buy and Sell Stock IV.
*/

class Solution {
    public int maxProfit(int k, int[] prices) {
        int n = prices.length, K = k;
        int memo[][][] = new int[n + 1][K + 1][2];
        for (int i = 0; i <= n; i++) memo[i][0][1] = Integer.MIN_VALUE;
        for (int i = 0; i <= K; i++) memo[0][i][1] = Integer.MIN_VALUE;

        for (int i = 1; i <= n; i++) {
            for ( k = 1; k <= K; k++) {
                memo[i][k][0] = Math.max(memo[i - 1][k][0], memo[i - 1][k][1] + prices[i-1]);
                memo[i][k][1] = Math.max(memo[i - 1][k][1], memo[i - 1][k - 1][0] - prices[i-1]);
            }
        }


        return memo[n][K][0];
    }
}

解法一改进,动态规格,状态压缩

/*
状态压缩,通用形式
Runtime: 4 ms, faster than 82.54% of Java online submissions for Best Time to Buy and Sell Stock IV.
Memory Usage: 36.5 MB, less than 90.44% of Java online submissions for Best Time to Buy and Sell Stock IV.
* */
class SolutionV2 {
    public int maxProfit(int k, int[] prices) {
        int n = prices.length;
        int memo[][] = new int[k + 1][2];
        for (int i = 0; i <= k; i++) memo[i][1] = Integer.MIN_VALUE;

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= k; j++) {
                memo[j][0] = Math.max(memo[j][0], memo[j][1] + prices[i-1]);
                memo[j][1] = Math.max(memo[j][1], memo[j - 1][0] - prices[i-1]);
            }
        }
        return memo[k][0];
    }
}
Best Time to Buy and Sell Stock with Cooldown
/*
* 309. Best Time to Buy and Sell Stock with Cooldown
* 买股票,不限次数,冷冻期
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
* */

q122拓展

  • dp(n)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润
    d p ( n ) [ 0 ] = m a x ( d p ( n − 1 ) [ 0 ] , d p ( n − 1 ) [ 1 ] + p r i c e s [ n − 1 ] ) d p ( n ) [ 1 ] = m a x ( d p ( n − 1 ) [ 1 ] , d p ( n − 2 ) [ 0 ] − p r i c e s [ n − 1 ] ) dp(n)[0] = max(dp(n-1)[0], dp(n-1)[1] + prices[n-1])\\ dp(n)[1] = max(dp(n-1)[1], dp(n-2)[0] - prices[n-1]) dp(n)[0]=max(dp(n1)[0],dp(n1)[1]+prices[n1])dp(n)[1]=max(dp(n1)[1],dp(n2)[0]prices[n1])

  • 状态n

  • 选择,当前两个状态从何处发展而来

  • base,基值情况比较复杂,当n = 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。
    当n = -1时,与n=0作一块处理即可。因为主要是用到了dp[1][1]计算第一天,持有股票的可能,由于第一次买不可能有冷冻期,所以想n=0一样返回0即可

/*
Runtime: 1 ms, faster than 58.45% of Java online submissions for Best Time to Buy and Sell Stock with Cooldown.
        Memory Usage: 39 MB, less than 14.53% of Java online submissions for Best Time to Buy and Sell Stock with
*/

class Solution {
    private static int[][] memo;
    public int maxProfit(int[] prices) {
        int n = prices.length;
        memo = new int[n + 1][2];
        return dp(n, prices)[0];
    }

    private int[] dp(int n, int[] prices) {
        if (n <= 0) {
            memo[0][1] = Integer.MIN_VALUE;
            return memo[0];
        }
        if(memo[n][0]!=0) return memo[n];
        memo[n][0] = Math.max(dp(n - 1, prices)[0], dp(n - 1, prices)[1] + prices[n-1]);
        memo[n][1] = Math.max(dp(n - 1, prices)[1],  dp(n-2,prices)[0] - prices[n - 1]);

        return memo[n];
    }
}
/*
迭代,状态压缩
Runtime: 0 ms, faster than 100.00% of Java online submissions for Best Time to Buy and Sell Stock with Cooldown.
Memory Usage: 36.8 MB, less than 75.51% of Java online submissions for Best Time to Buy and Sell Stock with
* */
public class SolutionV1_1 {
    public int maxProfit(int[] prices) {
        int n = prices.length, dp_i_0, dp_i_1,dp_ip2_0, t;
        dp_i_0 = dp_ip2_0 = 0;dp_i_1 = Integer.MIN_VALUE;
        for (int i = 1; i <= n; i++) {
            t = dp_i_0;
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i - 1]);
            dp_i_1 = Math.max(dp_i_1, dp_ip2_0 - prices[i - 1]);
            dp_ip2_0 = t;
        }
        return dp_i_0;
    }
}
Best Time to Buy and Sell Stock with Transaction Fee
/*
* 714. Best Time to Buy and Sell Stock with Transaction Fee
* 买股票,多次购买,交易费
* https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
* */

q122拓展

  • dp(n)包含两个状态,求取[0]代表没持有股票,[1]代表持有股票情况下能得到的利润
    d p ( n ) [ 0 ] = m a x ( d p ( n − 1 ) [ 0 ] , d p ( n − 1 ) [ 1 ] + p r i c e s [ n − 1 ] ) d p ( n ) [ 1 ] = m a x ( d p ( n − 1 ) [ 1 ] , d p ( n − 1 ) [ 0 ] − p r i c e s [ n − 1 ] − f e e ) dp(n)[0] = max(dp(n-1)[0], dp(n-1)[1] + prices[n-1])\\ dp(n)[1] = max(dp(n-1)[1], dp(n-1)[0] - prices[n-1] - fee) dp(n)[0]=max(dp(n1)[0],dp(n1)[1]+prices[n1])dp(n)[1]=max(dp(n1)[1],dp(n1)[0]prices[n1]fee)
  • 状态n
  • 选择,当前两个状态从何处发展而来
  • base,基值情况比较复杂,当n == 0时,代表没有一支股票,而dp[0][1]代表持有——这是不可能的。根据状态转移公式,这个需要定义为-INF,否则会出现错误。
/*
Runtime: 3 ms, faster than 93.90% of Java online submissions for Best Time to Buy and Sell Stock with Transaction Fee.
        Memory Usage: 48.2 MB, less than 68.41% of Java online submissions for Best Time to Buy and Sell Stock with
*/

class Solution {
    public int maxProfit(int[] prices, int fee) {
        int n = prices.length, dp_i_0 = 0, dp_i_1 = Integer.MIN_VALUE,t;
        for (int i = 1; i <= n; i++) {
            t = dp_i_0;
            dp_i_0 = Math.max(dp_i_0, dp_i_1 + prices[i-1]);
            dp_i_1 = Math.max(dp_i_1, t - prices[i - 1] - fee);
        }
        return dp_i_0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值