力扣-121-买卖股票的最佳时机

121-买卖股票的最佳时机

2022-10-27 重做

这里能重复买入卖出吗?不能,这里只考虑一次获利

动态规划的思路是什么?一个dp[i]代表截至第i天的最大收益

由于卖出获利一定在买入之后,即买入是前提,然后考虑低买高卖
定义一个变量min表示前i天的最低价,它随着遍历动态更新,那么:今天卖出获利是prices[i]-min,今天之前卖出最大获利是dp[i-1]
那么i天的最大获利就是dp[i] = max(dp[i-1],prices[i]-min)
初始化第一天的获利-price[0],因为即时第二天卖出也不可能获利比第一天的负值更低
初始化最小值为price[0]

第一版能过得代码:

int maxProfit(vector<int>& prices) {
	
	int n = prices.size();
	vector<int> dp(n);
	
	int minPrice = prices[0];
	dp[0] = -prices[0];

	for (int i = 1; i < n; i++) {
		dp[i] = max(dp[i - 1], prices[i] - minPrice);
		if (prices[i] < minPrice) minPrice = prices[i];
	}

	return dp[n - 1] > 0 ? dp[n - 1] : 0;
}

所以关键是这个最低的价格吗?

另外代码看起来总觉得不够优雅,另外官方的题解2其实就是空间优化后的动态规划,那么这里也考虑这两个方面尝试优化
空间优化

首先这个dp数组就可以优化掉,我只要知道截至目前最大的收益,和截至前一天最大的收益

这样定义的一维数组dp一般都可以这么空间优化

int maxProfit(vector<int>& prices) {
	
	int minPrice = prices[0],preProfit = -prices[0],res;

	for (int i = 1; i < prices.size(); i++) {
		res = max(preProfit, prices[i] - minPrice);
		preProfit = res;
		if (prices[i] < minPrice) minPrice = prices[i];
	}

	return res>0?res:0;
}

总感觉还有待优化,是思路上的
啊,这,我才看到,既然preProfit = res;这就直接赋值了,不如直接删了
真好,又少了一个变量

int maxProfit(vector<int>& prices) {
	
	int minPrice = prices[0],res = -prices[0];

	for (int i = 1; i < prices.size(); i++) {
		res = max(res, prices[i] - minPrice);
		if (prices[i] < minPrice) minPrice = prices[i];
	}

	return res>0?res:0;
}

题解1,动态规划思路

        int maxProfit(vector<int>& prices) {
        int len = prices.size();
        vector<vector<int>> dp(2,vector<int>(len));

        dp[0][0] = 0;
        dp[1][0] = -prices[0];

        for (int i = 1; i < prices.size(); ++i) {
            dp[0][i] = max(dp[0][i - 1], dp[1][i - 1] + prices[i]);
            // 如果当天没持有,那么要么是没买也没卖(之前就已经买卖过或者压根没买过),要么当天全部卖出了
            // 要么等于前一天没持有的收益/要么等于前一天持有的收益(负值)+当天卖出的收益
            dp[1][i] = max(dp[1][i - 1], -prices[i]);
            // 如果当天持有了,要么之前买了当天没卖,要么当天买了
            // 要么等于前一天持有的收益/当天买入的收益(负值)
        }
        return dp[0][len - 1];
        // 卖出(不持有)才可能收益最大
        // -1是因为是从0开始的

可以发现不管是时间还是空间效率都很低

但其实可以发现定义的二维数组dp一维只使用了0和1两种数值,那么可以使用二维而使用两个一维数组来实现简化

        int maxProfit(vector<int>& prices) {
        int len = prices.size();

        vector<int> nohold(len),hold(len);
        nohold[0] = 0;
        hold[0] = -prices[0];

        for (int i = 1; i < prices.size(); ++i) {
            nohold[i] = max(nohold[i-1], hold[i-1] + prices[i]);
            hold[i] = max(hold[i-1], -prices[i]);
        }
        return nohold[len-1];

多少还是有点进步

官方题解,依次遍历

这就很巧妙了,很简洁,效率也不错

        int minProfit = 1e9,maxProfit=0;
        for(int price:prices){
            minProfit = min(price,minProfit);
            maxProfit = max(maxProfit,price-minProfit);
            // 很明显这里每次计算最大收益时用的最小成本并不是所有价格中最便宜的
            // 而仅仅是已经遍历过的中最便宜的
            // 结果是对的
            // 因为如果想要计算收益,那么必然有卖出的动作,那么必然是在此之前买入的
            // 那么要实现收益最大只能是使用此前的最低成本
        }
        return maxProfit;

posted @ 2022-03-25 12:36  YaosGHC  阅读(61)  评论(0)    收藏  举报