714. Best Time to Buy and Sell Stock with Transaction Fee

714. Best Time to Buy and Sell Stock with Transaction Fee

1. 题目描述
题目链接

Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.
You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)
Return the maximum profit you can make.
Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1
Selling at prices[3] = 8
Buying at prices[4] = 4
Selling at prices[5] = 9
The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

2. 题目分析
购入购票和售出股票的次数不限,但售出时需要手续费fee,要求,找到获得的最大利润。那么,我们售出的时机,就是不但要求,售出的价值大于(购入时的价值+手续费fee),而且,还要保证没有出售早了。比如说1, 3, 2, 8, 4, 9,但第二天时,满足售出的价值大于(购入时的价值+手续费fee),如果这时候出售,那么第三天2买入的话,第四8出售,那么总利润,(3-1-fee)+(8-2-fee)=4 小于(8-1-fee)=5,说明卖早了。
那么怎样才算出售早了呢?这点应该是最难考虑的地方了,既然日后总有可能存在满足条件售出的价值大于(购入时的价值+手续费fee)的日期,那么我们干脆从另一个角度出发,出售完股票不是还要重新买入股票吗?那我们考虑什么时候适合重新买入股票,就是买入股票的前一天i-1的股票价值大于(第i天购入时的价值+手续费fee),那么就可以在第i-1天出售这股票,然后在第i天重入购入股票。

3. 解决思路

  • 贪心算法,上面的题目分析是一种贪心算法的思路,每次在局部找到最优解。我也不确定,毕竟这思路不是我自己想出来的,当时虽然找到了这个条件:售出的价值大于(购入时的价值+手续费fee),但一直没找到出售早了的限定条件,所以就去看别人的解题思路。。。但这想法好像是有点贪心算法的味道。
  • 动态规划:
    对于第i天的收益,可能有两种情况:手中有stock和手中没有stock
    如果手中没有stock,可能是今天刚卖出或者是保持前一天的状态 ,此时令收益为sold。
    如果手中有stock,可能是今天刚买入或者是保持前一天的状态,此时令收益为hold(收益有可能是负的)。
    因为第i天的状态仅和前一天的状态有关,所以只需要两个数组sold和hold。
    sold表示手上没有stock状态下的收益,hold表示手上有stock状态下的收益。
    解释如下:
    sold 该天结束手里没有股票的情况下,已经获得的最大收益;
    hold 该天结束手里有股票的情况下,已经获得的最大收益。
    所以转移状态分析如下:
    sold更新的策略是:既然今天结束之后手里没有股票,那么可能是今天没买(保持昨天的状态),也可能是今天把股票卖出了;
    hold 更新的策略是:今天今天结束之后手里有股票,那么可能是今天没卖(保持昨天的状态),也可能是今天买了股票。
    这里需要注意sold和hold的初始值,最终输出sold,因为最后一天的情况一定是手里没有stock的。

4. 代码实现(java)

  • 贪婪算法
package com.algorithm.leetcode.dynamicAllocation;

/**
 * Created by 凌 on 2018/12/15.
 * 描述:714. Best Time to Buy and Sell Stock with Transaction Fee
 */
public class MaxProfit_upgradedVersion {
    public static void main(String[] args) {
//        int[] prices = {1, 3, 2, 8, 4, 9};
        int[] prices = {1, 3, 2, 8,9,4};
        int fee = 2;
        int result = maxProfit_greedy(prices,fee);
        System.out.println(result);
    }
    
    /**
     * 贪心算法
     * @param prices
     * @param fee
     * @return
     */
    public static int maxProfit_greedy(int[] prices, int fee) {
        int profit = 0;
        int curProfit = 0;
        int minP = prices[0];
        int maxP = prices[0];
        for (int i = 1; i < prices.length; i++) {
            minP = Math.min(minP, prices[i]);
            maxP = Math.max(maxP, prices[i]);
            curProfit = Math.max(curProfit, prices[i] - minP - fee);
            //当买入股票的前一天i-1的股票价值大于(第i天购入时的价值+手续费fee),那么就可以在第i-1天出售这股票,然后在第i天重入购入股票
            if ((maxP - prices[i]) >= fee) {
                profit += curProfit;
                curProfit = 0;
                minP = prices[i];
                maxP = prices[i];
            }
        }
        //curProfit记录了当前一次交易能得到的最大收益,只有当maxP-prices[i]>=fee时,才将curProfit累加到总的收益中。
        // 最后一次交易不需要考虑是否早卖了,所以直接累加最后一次的curProfit。
        return profit + curProfit;
    }

}

  • 动态规划算法
/**
     * 动态规划
     * @param prices
     * @param fee
     * @return
     */
    public static int maxProfit_dp(int[] prices, int fee) {
        if (prices == null || prices.length < 2){
            return 0;
        }
        int len = prices.length;

        /**
         * 第i天,如果手中有stock
         * hold[i]表示第i天,你保持这份股票,你可以获得的最大利润,
         * 第i天有两种决定,继续保持这份股票,买入这份股票;
         * 如果第i天保持这股票,那么hold[i] = hold[i-1],
         * 如果第i天买入这股票(说明前一天已经出售这只股票了),那么hold[i] = sold[i-1] - prices[i]
         * hold[i] = max(hold[i-1], sold[i-1] - prices[i])
         */
        int[] hold = new int[len];

        /**
         * 第i天,如果手中没有stock
         * sold[i]表示第i天,你出售这份股票,你可以获得的最大利润
         * 第i天有两种决定,继续保持这份股票,出售这份股票;
         * 如果第i天保持这股票,那么sold[i] = sold[i-1],
         * 如果第i天出售这股票,那么sold[i] = hold[i-1] + prices[i] - fee
         * sold[i] = max(sold[i-1], hold[i-1] + prices[i] - fee)
         */
        int[] sold = new int[len];

        hold[0] = -prices[0];
        for (int i = 1; i < len; ++i) {
            hold[i] = Math.max(hold[i - 1], sold[i - 1] - prices[i]);
            sold[i] = Math.max(sold[i - 1], hold[i - 1] + prices[i] - fee);
        }
        return sold[len - 1];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值