Best Time to Buy and Sell Stock

题目来源于leetcode  Best Time to Buy and Sell Stock IV.  实质与求最大连续k个子区间和问题类似。
Say you have an array for which the i th element is the price of a given stock on day i.  Design an algorithm to find the maximum profit. You may complete at most k  transactions. You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).


题目说是最多可交易k次,因为我们可以在同一天买进卖出多次,所以对于不足k次交易的我们总是可以扩展成交易k次,从而只需考虑正好交易k次的情况。 用动态规划做。dp[i][j]表示最后一宗交易在第j天完成,并在此期间共完成i次交易的最大利润, 关键递推式:dp[i][j] = max{ max_(0<=t<j){dp[i][t]+prices[j]-prices[t]}, max_(0<=t<=j){dp[i-1][t] + max_(t<=m<=j){prices[j]-prices[m]} }  ,用到的关键子式有dp[i][t], dp[i-1][t],这些都是在dp[i][j]之前已经得到的结果。因此最直接、可读性最强的算法是


int maxProfit(int k, vector<int> &prices){
	int res = 0;
	int n = prices.size();
	k = min(k,n-1);
	//dp[i][j]为最后一宗交易在第j天完成,并在此期间共完成i次交易的最大利润
	int **dp = new int*[k+1];
	for(int i=0;i<=k;i++){ 
		dp[i] = new int[n];
		dp[i][0] = 0;
	}

        memset(dp[0],0,n*sizeof(dp[0]));
   
	for(int i=1;i<=k;i++){
		for(int j=1;j<n;j++){ 
			//dp[i][j] = max{ max_(0<=t<j){dp[i][t]+prices[j]-prices[t]}, max_(0<=t<=j){dp[i-1][t] + max_(t<=m<=j){prices[j]-prices[m]} }
			int curMax = dp[i-1][j];
            int maxDif = 0; //记录max(prices[j]-prices[t])
			for(int t=j-1;t>=0;t--){
				curMax = max(curMax, dp[i][t]+prices[j]-prices[t]);
				maxDif = max(maxDif,prices[j]-prices[t]);  //更新maxDif
				curMax = max(curMax, dp[i-1][t]+maxDif);
			}

			dp[i][j] = curMax;
			res = max(res,dp[i][j]);
		}
	}

	for(int i=0;i<=k;i++) delete dp[i];
	delete []dp;

	return res;
}

但是上述算法空间复杂度为O(k*n),会导致MLE。事实上我们并不需要开辟这么大的一个二维数组,只需两个一维数组就可以了,这样可以大大节省空间。

int maxProfit(int k, vector<int> &prices){
	int res = 0;
	int n = prices.size();
	k = min(k,n-1);
	//dp[i][j]为最后一宗交易在第j天完成,并在此期间共完成i次交易的最大利润
	int *last = new int[n], *cur = new int[n];
    for(int i=0;i<n;i++) last[i] = 0; 
    cur[0] = 0;

	for(int i=1;i<=k;i++){
		for(int j=1;j<n;j++){ 
			//dp[i][j] = max{ max_(0<=t<j){dp[i][t]+prices[j]-prices[t]}, max_(0<=t<=j){dp[i-1][t] + max_(t<=m<=j){prices[j]-prices[m]} }
			int curMax = last[j];
            int maxDif = 0; //记录max(prices[j]-prices[t])
			for(int t=j-1;t>=0;t--){
				curMax = max(curMax, cur[t]+prices[j]-prices[t]);
				maxDif = max(maxDif,prices[j]-prices[t]);  //更新maxDif
				curMax = max(curMax, last[t]+maxDif);
			}
            
			cur[j] = curMax;
			res = max(res,cur[j]);
		}
		for(int j=0;j<n;j++) last[j] = cur[j];
	}

	delete []cur;
	delete []last;

	return res;
}

MLE解决了,但时间复杂度O(k * n^2),仍然TLE,仔细观察下其实可以去掉第三个循环。dp[i][j] = max(max1, max2) + prices[j],max1、max2可以看成关键递推式的前后两项,但不完全相同,这里把prices[j]剥离出来,是为了每次求dp[i][j]可以充分利用在求dp[i][j-1]时已经求好的max1、max2,以避免多余的计算。这样时间复杂度就降低一个阶,为O(k*n)。k可能会接近于甚至远大于n,可以特别考虑这种情况,以避免多余的计算,因此算法时间复杂度不会高于O(n^2)。

int maxProfit(int k, vector<int> &prices){
	
	int res = 0;
	int n = prices.size();
	int mk = 0,i;
	for(i=1;i<n;i++){
		if(prices[i]>prices[i-1]){ res += prices[i]-prices[i-1]; mk++;}
		if(mk>k){ res = 0; break; }
	}
	if(i>=n) return res;

	//dp[i][j]为最后一宗交易在第j天完成,并在此期间共完成i次交易的最大利润
	int *last = new int[n], *cur = new int[n];
    for(i=0;i<n;i++) last[i] = 0; 
    cur[0] = 0;
	
	for(i=1;i<=k;i++){
		/* max1记录 max_(0<=t<j){dp[i][t]-prices[t]}
		   max2记录 max_(0<=t<=j){dp[i-1][t] - min_(t<=m<=j){prices[m]} 
		   max1,max2与prices[j]无关。 dp[i][j] = max(max1 + prices[j], max2 + prices[j]) */
	   
        int max1 = cur[0]-prices[0], max2 = last[0]-prices[0];
		int maxj = last[0]; 
		
		for(int j=1;j<n;j++){
			maxj = max(maxj,last[j]); 
			max1 = max(max1, cur[j-1]-prices[j-1]);   //更新max1 
			max2 = max(max2, maxj-prices[j]);

			cur[j] = max(max1,max2) + prices[j]; 
			res = max(res,cur[j]);
		}
		for(int j=1;j<n;j++) last[j] = cur[j];
	}

	delete []cur;
	delete []last;

	return res;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值