zoukankan      html  css  js  c++  java
  • 买卖股票的最佳时期

    Best Time to Buy and Sell Stock I

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润

    注意你不能在买入股票前卖出股票

    示例 1:

    输入: [7,1,5,3,6,4]
    输出: 5
    解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
         注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。
    

    示例 2:

    输入: [7,6,4,3,1]
    输出: 0
    解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。

    分析:
    贪心法,分别找到价格最低和最高的一天,低进高出,注意最低的一天要在最高的一天之前。
    把原始价格序列变成差分序列,本题也可以做是最大 m 子段和, m = 1。
     int maxProfit(vector<int>& prices) {
            //现在最低的时候买入,再在最高的时候卖掉
            /*把原始价格序列变成差分序列,本题也可以做是最大 m 子段和, m = 1。*/
            
            if(prices.size()<2) return 0;
            
            int profit=0;
            int cur_min=prices[0];
            for(int i=1;i<prices.size();++i){
                profit=max(profit, prices[i]-cur_min);
                cur_min=min(cur_min, prices[i]);
            }
            return profit;      
    }

    买卖股票的最佳时机 II

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。

    设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。

    注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    分析:
    贪心法,低进高出,把所有正的价格差价相加起来。
    把原始价格序列变成差分序列,本题也可以做是最大 m 子段和, m = 数组长度。
    int maxpfrofit2(vector<int> prices)
    {
        int sum = 0;
        for (int i = 1; i < prices.size(); ++i){
            if (prices[i] - prices[i - 1]>0)
                sum += prices[i] - prices[i - 1];
        }
        return sum;
    }

    买卖股票的最佳时机 III

    给定一个数组,它的第 i 个元素是一支给定的股票在第 天的价格。

    设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

    注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    分析:

    设状态 f(i) ,表示区间[0, i](0 ≤ i n - 1)的最大利润,状态 g(i) ,表示区间[i- 1](0 ≤ ≤ - 1)的最大利润,则最终答案为

    max {f(i) + g(i)} , 0 ≤ i n - 1
    允许在一天内买进又卖出,相当于不交易,因为题目的规定是最多两次,而不是一定要两次。将原数组变成差分数组,本题也可以看做是最大 子段和, m=2 


    以第i天为分界线,计算第i天之前进行一次交易的最大收益preProfit[i],和第i天之后进行一次交易的最大收益postProfit[i],最后遍历一遍,max{preProfit[i] + postProfit[i]} (0≤i≤n-1)就是最大收益。

    方法一:

    int maxProfit(vector<int>& prices) {
      //其实也就是找到两个最大子数组和,和I题差不多。
      //先求出第一个最大子数组,避开第一个最大子数组的情况下,再求出第二大子数组,再相加即可。
      if(prices.size()<2) return 0;
    
      int n=prices.size();
      vector<int> maxprofit1(n,0), maxprofit2(n,0);
      int maxprofit=0;
    
      for(int i=1, cur_min1=prices[0]; i<n; ++i){
        cur_min1=min(cur_min1, prices[i]);
        maxprofit1[i]=max(maxprofit1[i-1], prices[i]-cur_min1); 
      }
    
      for(int i=n-2, cur_max2=prices[n-1]; i>=0; --i){
        cur_max2=max(cur_max2, prices[i]);
        maxprofit2[i]=max(maxprofit2[i], cur_max2-prices[i]);
      }
    
      for(int i=0;i<n;++i)
        maxprofit=max(maxprofit, maxprofit1[i]+maxprofit2[i]);
      return maxprofit;
    }

    方法二:

      int maxProfit(vector<int>& prices) {   
        
    //buy1:买第一次手里的钱;sell1卖出去一次手里的钱 = max(buy1+卖出当前股票价格的钱,上一轮卖出第一笔股票后手里剩的钱);    
        
    //buy2在该价格买入第二次手里的钱 = max(上一轮买入第二笔股票后手里剩的钱,sell1-当前点股票价格)
        
    //sell2在该价格卖出第二次后手里剩的钱 = max(上一轮卖出第二笔股票后手里剩的钱, buy2+当前股票价格)
    int buy1=INT_MIN, buy2=INT_MIN; int sell1=0,sell2=0; for(int i=0;i<prices.size();i++){ sell2=max(sell2, buy2+prices[i]); buy2=max(buy2, sell1-prices[i]); sell1=max(sell1, buy1+prices[i]); buy1=max(buy1, -prices[i]); } return sell2; }

    买卖股票的最佳时机 IV

    给定一个数组,它的第 i 个元素是一支给定的股票在第 天的价格。

    设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易

    注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)

    分析:
    用一个局部最优解和全局最有解表示到第i天进行j次的收益,特殊的动态规划。local[i][j]为在到达第i天时最多可进行j次交易并且最后一次交易在最后一天卖出的最大利润,此为局部最优。定义global[i][j]为在到达第i天时最多可进行j次交易的最大利润,
    此为全局最优。他们的递推公式为:diff = prices[i]-prices[i-1]
      local[i][j] = max(global[i - 1][j - 1] + max(diff, 0), local[i - 1][j] + diff)
      global[i][j] = max(local[i][j], global[i - 1][j])

    关于 local 的状态转移方程,取下面二者中较大的一个:
    1. 全局前 i-1 天进行了 j-1 次交易,然后然后加上今天的交易产生的利润(如果赚钱就交易,不赚钱就不交易,什么也不发生,利润是0
    2. 局部前 i-1 天进行了 j 次交易,然后加上今天的差价(local[i-1][j]是第 i-1 天卖出的交易,它加上diff后变成第 i 天卖出,并不会增加交易次数。无论 diff 是正还是负都要加上,否则就不满足 local[i][j] 必须在最后一天卖出的条件了)

    local[i][j]意味着在第i天一定有交易(卖出)发生,当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,

    即local[i][j]=local[i-1][j]+diff;当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]

    global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况:如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j];如果第i天有交易(卖出),那么global[i][j]=local[i][j]。

    • 动规所用的二维辅助数组可以降为一维的,即只用大小为k的一维数组记录到达第i天时的局部最优解和全局最优解。需要注意的是,由于第i天时交易k次的最优解依赖于第i-1天时交易k-1次的最优解,

    所以数组更新应当从后往前(即从k到1)更新。

    买卖股票的最佳时期带有一天的冷却期

    维护三个一维数组buy, sell,和rest。其中:
    buy[i]表示在第i天之前最后一个操作是买,此时的最大收益。
    sell[i]表示在第i天之前最后一个操作是卖,此时的最大收益。
    rest[i]表示在第i天之前最后一个操作是冷冻期,此时的最大收益。
    我们写出递推式为: 

    1. buy[i]  = max(rest[i-1] - price, buy[i-1])   
    2. sell[i] = max(buy[i-1] + price, sell[i-1])  
    3. rest[i] = max(sell[i-1], buy[i-1], rest[i-1])  

    上述递推式很好的表示了在买之前有冷冻期,买之前要卖掉之前的股票。一个小技巧是如何保证[buy, rest, buy]的情况不会出现,这是由于buy[i] <= rest[i], 即rest[i] = max(sell[i-1], rest[i-1]),这保证了[buy, rest, buy]不会出现。
    另外,由于冷冻期的存在,我们可以得出rest[i] = sell[i-1],这样,我们可以将上面三个递推式精简到两个:

    1. buy[i]  = max(sell[i-2] - price, buy[i-1])   
    2. sell[i] = max(buy[i-1] + price, sell[i-1])  

    由于i只依赖于i-1和i-2,所以我们可以在O(1)的空间复杂度完成算法:

      1. pre_buy = buy;【因为buy是上一轮的,此句含义:pre_buy = buy[i-1]】  
      2. buy = max(pre_buy, pre_sell - prices[i]);【此句含义:buy[i]即buy  =  max(buy[i-1]即pre_buy,sell[i-2]【尚未更新的即上一轮的pre_sell】 - prices[i])】  
      3. pre_sell = sell;【因为sell是上一轮的,此句含义:sell[i-1] = sell】  
      4. sell = max(pre_sell, pre_buy + prices[i]);【此句含义:sell[i]即sell   =   max(sell[i-1]即pre_sell,buy[i-1]【已更新为pre_buy了】 + prices[i])】  
      int maxProfit(vector<int>& prices) {
            int n=prices.size();
            if(n<2)  return 0;
            int buy=-prices[0], pre_buy=INT_MIN,sell=0,pre_sell=0;
            
            for(int i=1;i<n;i++){
                pre_buy=buy;
                buy=max(pre_buy, pre_sell-prices[i]);
                pre_sell=sell;
                sell=max(pre_sell, pre_buy+prices[i]);
            }
            return sell;
        }

    买卖股票的最佳时期带有K天的冷却期(ali的笔试题)












     

  • 相关阅读:
    VMware Workstation网卡不启动
    解决IE10以下对象不支持“bind”属性或方法
    二分法查找
    选择排序与冒泡排序
    方法内部开启线程的方法
    重写Collections实现自定义排序
    根据反射生成SQL语句
    vue插件安装备忘
    vue cli4.x 新建项目 过程提醒
    php setcooike()失败的原因之一,希望能帮到你
  • 原文地址:https://www.cnblogs.com/wang-130213/p/9037274.html
Copyright © 2011-2022 走看看