LeetCode 188. Best Time to Buy and Sell Stock IV

              是LeetCode 123的升级版,题意也差不多,只是限制了不能超过k次买卖。话说我做到第四题才发现Stock是股票的意思,stone才是石头...

              这道题做的挺坎坷了,用了好几种方法,做了一个晚上,最后还是看了题解稍微被提示一下才过的。

             一开始我就想到了动态规划的方法,我用d[p][i][j]  来表示区间[i, j] 间至多买卖p次的最大利润。递推公式也很简单,为

             d[p][i][j] = max(d[p][i][j] , d[p-1][i][q] + d[1][q+1][j] ) 。而d[1][i][j] 是可以先算出来的,算出来的复杂度为O(n2)。

             然而这种动态规划的时间复杂度为O(n4)不用交都知道会超时。不过我还是交了,反而是超出了内存空间。

             接下来我发现了d[p][i][j] 的第二维是没有必要的。然后我重新用一个新的数组a[i][j] 来表示区间[i, j] 买卖最多一次的最大利润,上面已经有提到,复杂度为O(n2)。而新的递推公式为,

             d[p][j] = max(d[p][j], d[p-1][i-1] + a[i][j] ) ,总的时间复杂度为O(n3)。

             然而还是超时了,超时的测试样例为一个k值很大的测试样例。我发现当k大于n/2时,多余的k并没有任何用处。所以我在开始的时候加上了一条 k = min (k , n/2 )。 但这依然改变了不了我超时的结果。

             受到四边形不等式的启发,我在想能不能减去第三维不必要的枚举呢?

             其实a[i][j] 在j增大的时候,并不是一直在增大。于是我用last[i][j] 来表示j后的第一个j ’ ,使得a[i][j'] > a[i][j]。这样j +1 ~ j’ - 1之间的枚举就能够省去了。计算所有last[i][j] 的时间复杂度为O(n2)。

             最后的时间复杂度虽然还是O(n3),但由于last数组的存在可以加速不少。

             再次提交发现不超时了,倒是内存反而超了...

             无奈之下去看了题解,发现当k足够大的时候,要用到LeetCode 122的方法来做,即不限制买卖次数的情况,真坑...

             修改之后终于过了,不过执行的时间基本属于倒数的了...而我在删去last数组之后再提交发现是超时的,说明这步的操作还是有必要的。代码如下,虽然感觉写的很糟糕,勉强能过而已:

const int maxk = 5000 + 5 ;
const int maxn = 5000 + 5;

# define INF 100000000

typedef vector<vector<int> > Matrix ;

Matrix d ;
Matrix a ;
Matrix last ;

class Solution {
public:
	int maxProfit2(vector<int>& prices) {
    	int n = prices.size() ;
        if (n <= 1) return 0;
    	
    	vector<int> a ;
    	a.push_back(INF) ;
    	for (int i=0; i<n; i++) a.push_back(prices[i]) ;
    	a.push_back(-INF) ;
    	
        int ans = 0 ;
        int buy = 0 ;
        
        for (int i=1; i<=n; i++) {
        	if (a[i] <= a[i-1]) buy = a[i] ;
        	else if (a[i] >= a[i+1]) {
        		ans += a[i] - buy ;
        	}
        }
        
        return ans ;    
    }
    
    
    int maxProfit(int k, vector<int>& prices) {
    	int n = prices.size() ;
        if (!n) return 0 ;
        if (!k) return 0 ;
        
        if (k > n/2) return maxProfit2(prices) ;
    	
    	d = Matrix(k+1) ;
    	a = Matrix(n) ;
    	last = Matrix(n) ;
    	
    	for (int i=0; i<=k; i++) {
    		for (int j=0; j<n; j++) d[i].push_back(0) ;
    	}
    	for (int i=0; i<n; i++) {
    		for (int j=0; j<n; j++) {
    			a[i].push_back(0) ;
    			last[i].push_back(-1) ;
    		}
    	}
    	
    //    memset(d, 0, sizeof(d)) ;
    //    memset(a, 0, sizeof(a)) ; 
         
	    for (int i=0; i<n; i++) {
        	int mx = prices[i] ;
        	for (int j=i+1; j<n; j++) {
        	    a[i][j] = max(a[i][j-1], prices[j] - mx) ;
        	    if (prices[j] < mx) {
        	    	mx = prices[j] ;
        	    }
        	} 
        }
        
        for (int j=n-1; j>=0; j--) {
        	int lx = j ;
        	int lv = a[j][j] ;
        	
        	for (int i=j; i>=0; i--) {
        		if (a[i][j] > lv) {
        			for (int t=lx; t>i; t--) {
        				last[t][j] = i ; 
        			}
        			lv = a[i][j] ;
        			lx = i ;
        		}
        	}
        }
        
        for (int p=1; p<=k; p++) {
        	for (int j=1; j<n; j++) {
        		d[p][j] = d[p-1][j] ;
        		
        		d[p][j] = max(d[p][j], a[0][j]) ;
        		int i = j ;
        		while (i != -1) {
        			d[p][j] = max(d[p][j], d[p-1][i-1] + a[i][j]) ;
        			i = last[i][j] ;
        		}
        	/*	for (int i=0; i<j; i++) {
        			d[p][j] = max(d[p][j], d[p-1][i] + a[i+1][j]) ;
        		}*/
        	}
        }
        
        return d[k][n-1] ;
         
    }
};



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值