力扣122(贪心+线性DP)

题目要求:

在这里插入图片描述
在这里插入图片描述

整体思路一:贪心算法

抓住一点,记录前序点,只要降价就卖,并且再次买入,只要涨价就等下一天,暂时不卖,就可以了。

具体代码一

class Solution {
public:
	int ans = 0;
	int pre = -1;//前序节点
	int mins = 1 << 30;//最低价
	int temp_ans = 0;
	int maxProfit(vector<int>& prices) {
		for (int i = 0; i < prices.size(); i++) {
			if (prices[i] < pre&&pre != -1) {//发生降价
				mins = prices[i];
				ans += temp_ans;
				temp_ans = 0;
			} 
			else if (pre == -1) {
				mins = prices[i];//第一个节点要更新最小值
			}
			else {
				temp_ans = prices[i] - mins;//涨价
			}
			pre = prices[i];//每次更新前序节点
		}
		ans += temp_ans;//防止最后一次涨价后,少计算一次收益
		return ans;
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

时间复杂度:O(N) :只需要遍历一遍数组即可

整体思路二:DP算法

1、博主在做这个题的时候出现了一个问题,将线性DP,区间DP,背包DP弄混了,咱们先说说咋区分它们:
<1>背包DP:这个最好判断,他最明显的特征是会给出一个限制,比如给0-1背包给一个最大的背包容量,或者采草药问题那种给一个时间限制,在这个限制之上,我们求解最大效益值,这是最明显的特征,其次会给出两组队列,这是第二个特征。有了他们两个特征作为判断,我们就可以较为轻松的判断出来背包问题。0-1背包+完全背包以及优化见我的博客背包问题
<2>线性DP,这种DP最明显的特征是给出一个线性序列,没有限制,但是要求我们在一个线性队列上操作(这个是最重要的特点),一般只要从前到后处理一遍就行了(并不全是这样),这一类的都可以归为线性DP,博主找到了另一位博主总结的经典线性DP合集,线性DP,肥肠滴银杏。
<3>区间DP:我们要在一个区间上求得最优值,区间不一定要以谁为起点,我们仅仅可以确定区间长度,区间的起点终点都不一定是谁,(这里明显的区别于线性DP)不断地由小到大枚举区间长度,大区间由小区间推出来,枚举所有可能的区间详见关路灯+石子合并
(这些只是个人目前的理解,这些只是可以帮我们更快速的确定解题思路,但一定还是要具体问题具体分析!有错误希望大家指出,谢谢)
2、再回到这题,我们很明显发现,给了一个序列,表示股价在每一天是多少,日子只能从前到后的过,每一天面临着是买入股票还是卖出股票,并且只能先买再卖,买入股票花钱,效益值为负,卖出股票效益值为正。
3、故综合上方分析:今天的状态只由昨天决定,明确的线性结构

具体代码2.1

1、这个版本没有空间优化
2、就两个状态,日子从前到后的过,所以第一维度i表示处理到谁了,每一天0没有股票,1持有股票。所以第二维j是否持有股票。
3、初始化时候,第一天占有股票,花钱了,资产为负(-prices[0]),第一天不占有股票,不花钱,资产为0.
4、状态转移:
<1>第i天不占有股票,由第i-1天占有(说明卖了,额外多得 prices[i])或者不占有股票转移而来
<2>第i天占有股票,由第i-1天占有或者不占有(说明买了,额外少得 prices[i])股票转移而来
5、找答案时候就是最后一天占有或者不占有二者取最大值即可。


class Solution {
public:
	int num = 0;
	int dp[30000][2] = {};//日子一天一天过,每一天我都可以买股票卖股票
	int maxProfit(vector<int>& prices) {
		num = prices.size();
		dp[0][0] = 0;//0代表不持有股票
		dp[0][1] = -prices[0];
		for (int i = 1; i < num; i++) {
			dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
			dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
		}
		return max(dp[num - 1][0], dp[num - 1][1]);
	}
};

(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):

在这里插入图片描述

具体代码2.2

1、开启空间优化:这个就像0-1背包问题似的,起始地一维没啥用,我就是按照线性顺序处理,所以第一维干掉就行,然后存在一个上一个状态数据被覆盖的问题,我们只需要题前拿出来存储一下就行了。
2、别的分析和上面一样。


class Solution {
public:
	int num = 0;
	int dp[2] = {};//日子一天一天过,每一天我都可以买股票卖股票
	int maxProfit(vector<int>& prices) {
		num = prices.size();
		dp[0] = 0;//0代表不持有股票
		dp[1] = -prices[0];
		for (int i = 1; i < num; i++) {
			int one = dp[0];//老状态题前存
			int two = dp[1];
			dp[0] = max(one, two + prices[i]);
			dp[1] = max(two, one - prices[i]);
		}
		return max(dp[0], dp[1]);
	}
};


(所有代码均已在力扣上运行无误)

经测试,该代码运行情况是(经过多次测试所得最短时间):
在这里插入图片描述

具体代码2.3(错误版)

我最开始走上了歪路,就想着用区间DP去解,在分析过后,用二维i,j分别表示区间起点终点,并且只能起点买终点卖(根据题意),大的区间由小区间转移而来,在众多小区间中找最值,最后如果能在区间首尾买卖,则再次比较一下就行。


class Solution {//错误版本,数据量太大,这个栈溢出了
public:
	int num = 0;
	int dp[30000][30000] = {};//这里会爆炸
	int maxProfit(vector<int>& prices) {
		num = prices.size();
		for (int l = 1; l < num; l++) {
			for (int i = 0, j; i + l < num; i++) {
				j = i + l;
				for (int k = i; k < j; k++) {
					dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]);//枚举小区间,找到小区间使之和最大
				}
				if (prices[i] < prices[j]) {
					dp[i][j] = max(dp[i][j], prices[j] - prices[i]);//如果区间首尾可以买卖,再次判断
				}
			}
		}
		return dp[0][num - 1];
	}
};



错误之处:
1、这个题他给的序列只能从前到后,当前状态就是从上一个转移过来,区间DP就会疯狂的枚举一大堆没用的根本用不上的区间,复杂度太高了。
2、数据量太大了,题目测得数据很严格,光开二维数组就栈溢出了。
3、综上它并不适合区间DP。

时空复杂度:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLU_LYM

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值