基本时间序列型
文章目录
- 基本时间序列型
- 动态规划的特点
- 那么什么是基本时间序列型
- 特点
- 套路
- [198. 打家劫舍](https://leetcode-cn.com/problems/house-robber/)
- [213. 打家劫舍 II](https://leetcode-cn.com/problems/house-robber-ii/)
- [122. 买卖股票的最佳时机 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
- [123. 买卖股票的最佳时机 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)
- [188. 买卖股票的最佳时机 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)
- [309. 最佳买卖股票时机含冷冻期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/)
动态规划的特点
- 无后效性
1.一旦dp(i,j)确定,就不用关心“我们如何计算出dp(i,j)”。
想要确定dp(i,j),只需要知道dp(i-1,j)和f(i,j-1)的值,而至于它们
是如何算出来的,对当前或之后的任何子问题都没有影响。
“过去不依赖将来,将来不影响过去” ——智巅语录
关键就是过去不依赖将来,将来不影响过去
- 最优子结构
dp(i,j)
的定义就已经蕴含了“最优”。
大问题的最优解可以由若干个小问题的最优解推出。(max,min, sum…)
那么什么是基本时间序列型
特点
给出一个序列(数组/字符串),其中每一个元素认为是“一天”,并且“今天的状态”只取决于昨天的状态
也就是可以用0代表操作后的一个状态,1代表另一个操作后的状态,…以此类推
重点在于归纳出这些状态的表达
套路
● 定义dp[i][j]:表示第i-th轮的第j种状态 (j=1,2,...,K)
● 千方百计将dp[i][j]与前一轮的状态dp[i-1][j]产生关系(j=1,2,...,K)
● 最终的结果是dp[last][j]中的某种aggregation (sum, max, min …)
状态的设计,有关于的词语就是有行使某种权利的能力
198. 打家劫舍
难度中等1488
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
class Solution {
public:
int rob(vector<int>& nums) {
// 时间序列型dp问题
// 特点就是,只和上一轮有关系。
// 选择 0:不偷 1: 偷
// 所以定义dp就是 dp[i][j]就是第i轮选择为j的时候偷取到的最高的金额
/*
定义状态转移方式
dp[i][0] = max(dp[i-1][0],dp[i-1][1]);
dp[i][1] = max(dp[i-1][0]+nums[i])
初始化
dp[0][0]=0;
dp[0][1] = nums[0]
*/
vector<vector<int>> dp(nums.size(),vector<int>(2,0));
dp[0][1] = nums[0];
for(int i=1;i<nums.size();i++) {
dp[i][0] = max(dp[i-1][0],dp[i-1][1]);
dp[i][1] = dp[i-1][0]+nums[i];
}
return max(dp[nums.size()-1][0],dp[nums.size()-1][1]);
}
};
213. 打家劫舍 II
难度中等691
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [0]
输出:0
class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()<2) {
return nums[0];
}
// 第一间房子不抢
vector<vector<int>> dp1(nums.size(),vector<int>(2,0));
for(int i=1;i<nums.size();i++) {
dp1[i][0]=max(dp1[i-1][0],dp1[i-1][1]);
dp1[i][1]=dp1[i-1][0]+nums[i];
}
// 最后一间房子不抢
vector<vector<int>> dp2(nums.size(),vector<int>(2,0));
for(int i=1;i<nums.size();i++) {
dp2[i][0]=max(dp2[i-1][0],dp2[i-1][1]);
dp2[i][1]=dp2[i-1][0]+nums[i-1];
}
return max(dp1[nums.size()-1][0],max(dp1[nums.size()-1][1],max(dp2[nums.size()-1][0],dp2[nums.size()-1][1])));
}
};
122. 买卖股票的最佳时机 II
难度简单1244
给定一个数组 prices
,其中 prices[i]
是一支给定股票第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: prices = [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
class Solution {
public:
int maxProfit(vector<int>& prices) {
/*
分析: 首先看看能不能满足最优子结构和无后效性
最优子结构: dp[i][0]: 代表第i轮手里还有股票的时候最优的解
dp[i][1]: 代表的是手里没有股票时候的最优解,所以满足最优子结构
而且
第i轮的营收值取决于i-1轮不影响后面的。 这也就是,现在取决于过去但不影响将来
*/
vector<vector<int>> dp(prices.size(),vector<int>(2,0));
// 初始化
dp[0][0]=0;
dp[0][1]=-prices[0];
for(int i=1;i<prices.size();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 dp[prices.size()-1][0];
}
};
123. 买卖股票的最佳时机 III
难度困难793
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1]
输出:0
class Solution {
public:
int maxProfit(vector<int>& prices) {
/*
分析和买卖股票II是一样的就不多累赘了
唯一的区别就是状态
0: 手上有第一支股票获得的最大利润
1: 手上卖完第一支股票无股票状态的最大利润
2: 手上有股票状态,买入第二支股票最大利润
3 : 手上无股票,卖了第二只股票后获得的最大利润
*/
vector<vector<int>> dp(prices.size(),vector<int>(4,INT_MIN));
// 初始化
dp[0][0]=-prices[0];
dp[0][1] = 0;
for(int i=1 ; i<prices.size();i++) {
dp[i][0] = max(dp[i-1][0],-prices[i]);
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+prices[i]);
dp[i][2] = max(dp[i-1][1]-prices[i],dp[i-1][2]);
dp[i][3] = max(dp[i-1][2]+prices[i],dp[i-1][3]);
}
// for(auto a : dp) {
// for(auto b :a) {
// cout<<b<<" ";
// }
// cout<<endl;
// }
return max(dp[prices.size()-1][1],dp[prices.size()-1][3]);
}
};
188. 买卖股票的最佳时机 IV
难度困难523
给定一个整数数组 prices
,它的第 i
个元素 prices[i]
是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
// 定义了一系列的状态
// 0: 有股票,第1轮交易
// 1: 无股票,第一轮交易后
// 2: 有股票,第2轮交易
// 3: 无股票,第二轮交易后
// .......
if(prices.empty()) {
return 0;
}
vector<vector<int>> dp(prices.size(),vector<int>(k*2,0));
// 初始化
for(int i=0;i<k;i++) {
dp[0][i*2]=-prices[0];
}
// cout<<"start"<<endl;
for(int i=1;i<prices.size();i++) {
for(int j=0;j<2*k;j++) {
if(j%2==0) { // 这时候这一轮手上是有股票的
if(j==0) {
dp[i][j]=max(dp[i-1][j],-prices[i]);
} else {
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]-prices[i]);
}
} else { // 这时候手里是没有股票的
dp[i][j]= max(dp[i-1][j],dp[i-1][j-1]+prices[i]);
}
}
}
// for(auto a: dp) {
// for(auto b : a) {
// cout<<b<<" ";
// }
// cout<<endl;
// }
// // cout<<"a"<<endl;
int res=0;
for(int i=0;i<k;i++) {
res= max(res,dp[prices.size()-1][i*2+1]);
}
return res;
}
};
309. 最佳买卖股票时机含冷冻期
难度中等791
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
- 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
- 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 这也是一个基本时间序列型号dp
/*
最优子结构: dp[i][j] 代表第i天,状态为j的最大收益
无后效性 : 现在只依赖过去的i-1天而且还不会影响将来
所以关键就在与寻找到这几种状态
0: 手里有股票的准柜台
1: 手里没有股票在冷冻期的状态
2: 手里没有股票不在冷冻期的状态
*/
vector<vector<int>> dp(prices.size(),vector<int>(3,0));
// 初始化
dp[0][0] = -prices[0];
for(int i=1;i<prices.size();i++) {
dp[i][0] = max(dp[i-1][0],dp[i-1][2]-prices[i]);
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+prices[i]);
dp[i][2]=dp[i-1][1];
}
// for(auto a : dp) {
// for(auto b:a) {
// cout<<b<<" ";
// }
// cout<<endl;
// }
return max(dp[prices.size()-1][1],dp[prices.size()-1][2]);
}
};