刷题刷题往死里刷。
121. 买卖股票的最佳时机
链接
思路:
二次做所以有思路了, 从头遍历数组,维持一个最小值,且遇到一个值就计算差值,且维护这个最大值为答案。
class Solution {
public int maxProfit(int[] prices) {
int min = prices[0], ans = 0;
for(int i = 0; i < prices.length; i++){
ans = Math.max(ans, prices[i] - min);
min = Math.min(min, prices[i]);
}
return ans;
}
}
122. 买卖股票的最佳时机 II
链接
思路:
由于可以在同一天买入卖出,可以将收入理解为所有利润和,有差的全部记录下来,没有的默认当天买入卖出。
class Solution {
public int maxProfit(int[] prices) {
//这个是求利润和
int len = prices.length;
int ans = 0;
for(int i = 1; i < len; i++){
if(prices[i] > prices[i - 1]){
ans+=(prices[i] - prices[i -1]);
}
}
return ans;
}
}
123.买卖股票的最佳时机III
自己的思路
可以将题目理解为找两段不相交的相差最大的数。
不会写
官方题解
感觉很合理但是又想不出来。
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int buy1 = -prices[0], sell1 = 0;//买入一个,已经完成一单(当天买入卖出)
int buy2 = -prices[0], sell2 = 0;//买了两个,已经完成两单(同一天买入卖出并再次卖如, 买入卖出两次)
for(int i = 1; i < n; i ++){
buy1 = Math.max(buy1, -prices[i]);//买入价格更低的
sell1 = Math.max(sell1, buy1 + prices[i]);//找差更高的价格
buy2 = Math.max(buy2, sell1 - prices[i]);//是否买入
sell2 = Math.max(sell2, buy2 + prices[i]);//是否卖出
}
return sell2;
}
}
收获
初始化
int buy1 = -prices[0], sell1 = 0;//买入一个,已经完成一单(当天买入卖出)
int buy2 = -prices[0], sell2 = 0;//买了两个,已经完成两单(同一天买入卖出并再次卖如, 买入卖出两次)
状态转移式子
buy1 = Math.max(buy1, -prices[i]);//买入价格更低的
sell1 = Math.max(sell1, buy1 + prices[i]);//找差更高的价格
buy2 = Math.max(buy2, sell1 - prices[i]);//是否买入
sell2 = Math.max(sell2, buy2 + prices[i]);//是否卖出
有点难,多理解
188.买卖股票的最佳时机
自己的思路
感觉跟上面那道题很像,改成k次循环而不是二次就好了。
跟官方题解的方法不一样,懒得看了。
class Solution {
public int maxProfit(int k, int[] prices) {
int[][] arr = new int[k+1][2];//k次买入卖出
for(int i = 0 ; i <= k; i++){
arr[i][0] = -prices[0];//buy初始化
arr[i][1] = 0;//当天买入卖出
}
int len = prices.length;
for(int i = 0; i < len; i++){
for(int j = 1; j <= k ; j++){
arr[j][0] = Math.max(arr[j][0], arr[j-1][1] - prices[i]);//等于上一次arr[j][1]剩下的前减价格
arr[j][1] = Math.max(arr[j][1], arr[j][0] + prices[i]);
}
}
return arr[k][1];
}
}
309.买卖股票的最佳时机含冷冻期
自己的思路
感觉不太会做,只是把上面那道题的k定成了len,并且修改了dp[i][0]的转移公式,如果我要买,我得买两天之前的。
但是答案是错误的,看看是不是初始化有问题。
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
int[][] dp = new int[len+1][2];
for(int i = 0; i <= len; i++){
dp[i][0] = -prices[0];
dp[i][1] = 0;
}
//那就当作买卖k次
for(int i = 2; i <= len; i++){
dp[i][0] = Math.max(dp[i][0] , dp[i-2][1] - prices[i-1]);//买或者不买
dp[i][1] = Math.max(dp[i][1], dp[i][0] + prices[i-1]);//表示利润的和,用加来表示
}
return dp[len][1];
}
}
官方题解
妙啊,既然买入卖出是两个状态,那完全可以把冷冻期也作为一个状态。
理解三个状态
f[i][0]: 手上持有股票的最大收益
f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
官方代码题解
class Solution {
public int maxProfit(int[] prices) {
int len = prices.length;
int[][] dp = new int[len][3];
dp[0][0] = -prices[0];
for(int i = 1; i < len; i++){
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]);//持有股票的最大利润
dp[i][1] = dp[i-1][0] + prices[i];//今天卖出票,冷冻期,不考虑当天买入卖出,持有股票的最大利润
dp[i][2] = Math.max(dp[i-1][1], dp[i-1][2]);//前一天是冷冻期或者不是冷冻期,反正前一天不可能持有股票,那是冷冻期的事
}
return Math.max(dp[len-1][2], dp[len-1][1]);//最后要注意比较,因为当天卖出的可能还没有传给最后的
}
}
收获
dp数组的设置
f[i][0]: 手上持有股票的最大收益
f[i][1]: 手上不持有股票,并且处于冷冻期中的累计最大收益
f[i][2]: 手上不持有股票,并且不在冷冻期中的累计最大收益
int[][] f = new int[n][3];
f[0][0] = -prices[0];
遍历方式
为什么这道题跟上一题的遍历方式不一样,没有将次数记录在状态里,只是在遍历n个股票,尝试找出最大的收益。
上一题要求的是恰好k次交易,所以需要记录交易次数。
递归方程
dp[i][0]
dp[i][0]表示在前i个股票中,手中持有股票的最大利润。
它的转移方程为持有前一天的股票,或者当天自己买入(原始钱是前一天的最大利润,很好理解,要持有股票的最大利润,就是拿前一天的最大利润买今天的)。即
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]);
dp[i][1]
dp[i][1]表示在前i个股票中,手中不持有股票的处于冷冻期的最大收益。
这个的转移比较难理解,多看。
手中不持有股票的冷冻期,意味着我前一天要卖出,不过前一天卖出的利益并不是最大利润,所以直接用前一天持有股票的最大利润减去前一天的股票价格,而不是继承。
- 为什么冷冻期是前一天的持有股票时的利润加今天卖出的股票价钱,不是加昨天的最大价钱,如果我当天是冷冻期的话,不是应该是昨天卖出的吗。
这里我们需要将冷冻期理解为卖出当天开始,为什么呢
- 之前的题目是可以多次在当天买入卖出的,但是在这道题,当天卖出了之后不能再买入了,所以可以理解为,这道题在当天卖出的时候就是冷冻期了,包括后面的第一天也是冷冻期。
所以dp[i][1] = dp[i-1][0] + price[i].
而由于当天不卖出的冷冻期,不需要对股票做任何操作
dp[i][2]
dp[i][2]表示在前i个股票,手中不持有股票的不处于冷冻期的最大收益。
不持有股票又不是冷冻期,意味着前一天是冷冻期或者前一天是冷冻期之后的日子,而当天是无法堆股票做任何操作的,所以只需要继承前两天的冷冻期或者非冷冻期的最大利润就可以了。
714.买卖股票的最佳时机含手续费
自己的思路
和无手续费的时候其实是一样的,只需要在收入减去费用就可以了,卖出和买入的时候的减费用都行。
跟官方题解没区别,过。
class Solution {
public int maxProfit(int[] prices, int fee) {
//一个普通的dp数组而已,只不过在卖出的时候减个交易费就好
int len = prices.length;
int[][] dp = new int[len][2];
dp[0][0] = -prices[0];
for(int i = 1 ; i < len; i++){
dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i]);//手中无股票的最大利润
dp[i][1] = Math.max(dp[i-1][1], dp[i][0] + prices[i] - fee);
}
return dp[len-1][1];
}
}