zoukankan      html  css  js  c++  java
  • 【算法笔记】买卖股票问题--DP/贪心算法

    给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
    设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
    注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

    示例 1:

    输入: p=[2,4,1], k = 2
    输出: 2
    解释: 在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
    

    示例 2:

    输入: p=[3,2,6,5,0,3], k = 2
    输出: 7
    解释: 在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
         随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
    

    分析

    当题目中(k)大于表格数量的(frac{1}{2})的时候,就相当于无限制了,这个时候采用贪心算法更好。否则就得用动态规划。

    动态规划

    根据上篇介绍动态规划设计的方法,(dp(p,k))应该代表着当数组为p时候最多完成k笔交易,所能获得的最大利润。然后找这个问题中股民遇到的选择情景,股民每一天都可以选择买与不买,卖与不卖,通过这些选择来使自己利益最大化,需要注意的是买与不买是在自己不持股票的状态下,卖与不卖是在自己持股的状态下。所以每一天的两种状态都在dp范围内。

    假设股民在i-1天之前在两种情况下都已经是做出了自己最优的选择了,那么在第i天我们应该如何做出抉择呢?
    我们假设(dp(i,j))代表包括第i天的前i天的股票给最大j笔交易机会,且当前不持股情况下,最大利润能是多少。很明显这和原问题是同样的结构。 这里包含了第i天的选择操作。
    我们再假设(hold(i,j))代表包括第i天的前i天的股票给最大j笔交易机会,且当前持股情况下,你目前的最大金钱数能是多少。这里和上面的区别在于,在这之前你是花了前买了一个股票的,至于是第i天买的还是更早买的,无所谓了。

    我们要让股民利益最大化,在第i天要做出选择:

    • 在当天不持股的情况下,说明要么他本来就不持股,要么说明他今天卖掉了自己的股票,消耗了一次交易机会,我们选择一个能让他利益最大的情况

    [dp(i,j)=max(dp(i-1,j),hold(i-1,j-1)+p[i]) ]

    • 在当天持股的情况下,说明要么他本来就持股,要么说明他今天买了股票,我们选择一个能让他利益最大的情况

    [hold(i,j)=max(hold(i-1,j),dp(i-1,j)-p[i]) ]

    写出表达式后我们要计算初始值:

    [dp(0,j)=0,j=0,1,2...k\ hold(0,j)=-p[0],j=0,1,2...k\ dp(i,0)=0,i=0,1,2...p.size-1\ ]

    贪心算法

    贪心比dp要容易,当(k)大于表格数量的(frac{1}{2})的时候,我们认为是无限制买卖。所以只要是涨价,我们都买。把原数列差分,然后把大于0的都加起来就是。

    c++代码

    class Solution {
    public:
        int maxProfit(int k, vector<int>& p) {
            vector<vector<int>> dp,hold;
            if(p.size()==0)return 0;
            if(k>p.size()/2){
                return greedy(p);
            }
            dp=vector<vector<int>>(p.size(),vector<int>(k+1,0));
            hold=vector<vector<int>>(p.size(),vector<int>(k+1,0));
    
            for(int i=0;i<=k;i++){
                hold[0][i]=-p[0];
            }
            for(int i=1;i < p.size();i++){
                hold[i][0]=max(hold[i-1][0],-p[i]);
            }
             
            for(int i=1;i<p.size();i++){
                for(int j=1;j<=k;j++){
                    dp[i][j]=max(dp[i-1][j],hold[i-1][j-1]+p[i]); 
                    hold[i][j]=max(hold[i-1][j],dp[i-1][j]-p[i]);
                }
            }
    
            return dp[p.size()-1][k];
        }
    
        int greedy(vector<int>& p){
            int ans=0;
            for(int i=0;i<p.size()-1;i++){
                ans+=max(0,p[i+1]-p[i]);
            }
            return ans;
        }
    };
    
  • 相关阅读:
    在osg的图形上贴一张纹理图片
    在vs下的osg的qt窗口开发例子以及一些注意事项
    几个排序算法
    UVa11988-破损的键盘 Broken Keyboard
    UVa 442-矩阵链乘 Matrix Chain Multiplication
    Uva 514-铁轨 Rails
    Uva 136-丑数 ugly number
    修改 Sublime 按快捷键 ctrl+s 自动格式化(reindent lines)的问题
    React Native 项目配置 Flow (windows环境)
    Redux-Form 基础使用
  • 原文地址:https://www.cnblogs.com/kidtic/p/14219861.html
Copyright © 2011-2022 走看看