408算法练习——买股票
买股票
一、问题描述
假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少?
示例 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。
0 <= 数组长度 <= 10^5
二、问题分析
要求之进行一次买卖,所以要找到差值最大的两个元素,而且要求小的元素在前,大的元素在后。
1、双指针法:通过两个指针遍历数组,遍历过程中固定最小值,然后另一个指针向后遍历时计算收益,最后找出收益最大的利润
2、动态规划:一个好理解的方法就是假设头天买入,次日就卖出,也就是说股票只保留一天,而且不管这次交易是否盈利,这样操作后,就得出了每天能挣到的利润(包括负利 润),因为题干要求只能进行一次买入和卖出,其实就是对每天的利润求最大子序和,也就是说,在存有每日利润的数组中,选出一个连续的子序,这个子序的和是最大的,这时候求出的结果就是最大收益。如果结果是负数,就取0。而求最大子序和的过程可以使用动态规划完成
代码
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 const int maxn = 100010; 5 int A[maxn]; 6 int main(){ 7 int n;//n表示元素个数 8 scanf("%d",&n); 9 for(int i=0;i<n;i++){ 10 scanf("%d",&A[i]); 11 } 12 //可就在原数组上进行修改,节省空间 13 for(int i=0;i<n;i++){ 14 if(i<n-1){ 15 A[i] = A[i+1]-A[i]; 16 } 17 } 18 A[n-1] = 0; 19 //计算A[]的最大子序和 20 int res = 0; 21 for(int i=1;i<n;i++){ 22 A[i] = max(A[i-1]+A[i],A[i]); 23 if(A[i]>res){ 24 res = A[i]; 25 } 26 } 27 printf("%d",res); 28 return 0; 29 }
3、真正意义上的动态规划
首先是动态规划的数组,这里情况很特殊需要两个动态数组,第一个数组是dp0[i]表示第i天持有股票时的利润,第二个数组dp1[i]表示第i天不持有股票时的利润。
持有股票的两种情况,这支股票可能是今天买的也可能是昨天或前面某一天买入的。
不持有股票的两种情况,这支股票可能是今天卖出的,也可能是昨天或前面某一天就卖出了。
递推公式
dp0,持有股票,有两种情况,昨天就持有,那么值就是dp0[i-1]的值,今天才持有,那么值就应该是今天的股价;所以买入时股票价格要尽可能的低,然而买入股票是在花钱,所以利润就是负值,那么就要取大值,dp0[i]=max(dp0[i-1],-prise[i])
dp1,卖出股票,两种情况,昨天已经卖出,那么值就是dp1[i-1]的值,今天才卖出,那么值就是今天的股价加昨天的利润(即使是负值);卖出时股票价格要尽可能高,所以方程为dp1[i]=max(dp1[i-1],dp0[i-1]+prise[i])
详细分析状态转移方程
首先明确dp1和dp0中的值都是期望利润,所以都是要取最大值max。
持有股票dp0,这个数组表示的是买入股票的花销,花销也归入利润中,那么利润就是负支出,要注意,原则是利润总取最大值,不论正负。为了总保持利润最大所以就存在某天买入股票后一直握在手里(后面几天股票涨价了)所以此时后面几天的利润都和前面某一天一致。注意这不是真正的买股票,这只是根据已知结果的一种推演,真正操作是不可能预知股票涨跌的,不要带入生活。
不持有股票dp1,这个数组表示如果第i天不持有股票,那么所能得到的最大利润,所以这个也是结果数组。两种情况,今天卖股票不如昨天卖,所以此时就取dp[i]=dp[i-1]。今天卖股票比昨天利润多,所以dp[i]=dp[i-1]+prise[i]。这里应该是最难理解的地方,其实只要这样想,因为是今天才卖股票,那么昨天必然持有股票,所以要用到昨天持有股票时的利润dp0[i-1],今天卖出股票,所以今天的利润就是昨天的利润加今天的收入,就有dp[i]=dp[i-1]+prise[i]。
同理,针对每天不持有股票的情况有两种可能,股票已经卖出去了,股票今天才卖出去,这时对于昨天的利润就有不同的取值。这里必须理解,这样才能彻底理解整个动态规划的思想。
代码
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 const int maxn = 100010; 5 int main(){ 6 int A[maxn],dp0[maxn],dp1[maxn]; 7 int n; 8 scanf("%d",&n); 9 for(int i=0;i<n;i++){ 10 scanf("%d",&A[i]); 11 } 12 dp0[0] = -A[0];//规划边界,第一天持有股票的利润就是第一天股价的负值 13 dp1[0] = 0;//第一天不能卖出所以第一天不持有股票的利润就是0 14 for(int i=1;i<n;i++){ 15 dp0[i] = max(dp0[i-1],-A[i]); 16 dp1[i] = max(dp1[i-1],dp0[i-1]+A[i]); 17 } 18 printf("%d",dp1[n-1]); 19 return 0; 20 }
                    
                
                
            
        
浙公网安备 33010602011771号