本次题目
121 买卖股票的最佳时机(只能买卖一次) 122 买卖股票的最佳时机II(可以买卖多次) 123 买卖股票的最佳时机III(最多买卖两次) 188 买卖股票的最佳时机IV(最多买卖k次) 309 买卖股票的最佳时机含冷冻期(买卖多次,卖出有一天冷冻期) 714 买卖股票的最佳时机含手续费(买卖多次,每次有手续费)
121 买卖股票的最佳时机
class Solution {
public int maxProfit ( int [ ] prices) {
int low = Integer . MAX_VALUE;
int result = 0 ;
for ( int i = 0 ; i < prices. length; i++ ) {
low = Math . min ( low, prices[ i] ) ;
result = Math . max ( result, prices[ i] - low) ;
}
return result;
}
}
DP:
定义dp数组dp[i][0]表示第i天持有(不一定是当天买入)股票得到的最大利润,dp[i][1]表示表示第i天不持有股票得到的最大利润; 递推公式dp[i][0] = max(dp[i - 1][0], -prices[i]),dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]); 初始化dp[0][0] = -prices[0],dp[0][1] = 0; 遍历顺序正序; 举例。 补充:可以优化为滚动数组。
class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length == 1 ) return 0 ;
int [ ] [ ] dp = new int [ prices. length] [ 2 ] ;
dp[ 0 ] [ 0 ] = - prices[ 0 ] ;
dp[ 0 ] [ 1 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ i] [ 0 ] = Math . max ( dp[ i - 1 ] [ 0 ] , - prices[ i] ) ;
dp[ i] [ 1 ] = Math . max ( dp[ i - 1 ] [ 1 ] , dp[ i - 1 ] [ 0 ] + prices[ i] ) ;
}
return dp[ prices. length - 1 ] [ 1 ] ;
}
}
class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length == 1 ) return 0 ;
int [ ] [ ] dp = new int [ 2 ] [ 2 ] ;
dp[ 0 ] [ 0 ] = - prices[ 0 ] ;
dp[ 0 ] [ 1 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ 1 ] [ 0 ] = Math . max ( dp[ 0 ] [ 0 ] , - prices[ i] ) ;
dp[ 1 ] [ 1 ] = Math . max ( dp[ 0 ] [ 1 ] , dp[ 0 ] [ 0 ] + prices[ i] ) ;
dp[ 0 ] [ 0 ] = dp[ 1 ] [ 0 ] ;
dp[ 0 ] [ 1 ] = dp[ 1 ] [ 1 ] ;
}
return dp[ 1 ] [ 1 ] ;
}
}
122 买卖股票的最佳时机II
贪心之前实现过 。DP:同上121 买卖股票的最佳时机,但是可以买卖多次,因此持有股票的递推公式修改为dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i])。同样可优化为滚动数组。 DP优化:
class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length == 1 ) return 0 ;
int [ ] [ ] dp = new int [ 2 ] [ 2 ] ;
dp[ 0 ] [ 0 ] = - prices[ 0 ] ;
dp[ 0 ] [ 1 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ 1 ] [ 0 ] = Math . max ( dp[ 0 ] [ 0 ] , dp[ 0 ] [ 1 ] - prices[ i] ) ;
dp[ 1 ] [ 1 ] = Math . max ( dp[ 0 ] [ 1 ] , dp[ 0 ] [ 0 ] + prices[ i] ) ;
dp[ 0 ] [ 0 ] = dp[ 1 ] [ 0 ] ;
dp[ 0 ] [ 1 ] = dp[ 1 ] [ 1 ] ;
}
return dp[ 1 ] [ 1 ] ;
}
}
123 买卖股票的最佳时机III
最多买卖两次,因此当天可能有五种状态(不买卖,第一次买,第一次卖,第二次买,第二次卖)。 DP:
定义dp数组dp[i][j]表示第i天状态j得到的最大利润; 递推公式dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]),dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2]),dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]),dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]); 初始化dp[0][0] = 0,dp[0][1] = -prices[0],dp[0][2] = 0,dp[0][3] = -prices[0],dp[0][4] = 0; 遍历顺序正序; 举例。 补充:可以使用滚动数组优化。
class Solution {
public int maxProfit ( int [ ] prices) {
int [ ] [ ] dp = new int [ prices. length] [ 5 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = - prices[ 0 ] ;
dp[ 0 ] [ 2 ] = 0 ;
dp[ 0 ] [ 3 ] = - prices[ 0 ] ;
dp[ 0 ] [ 4 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ i] [ 1 ] = Math . max ( dp[ i - 1 ] [ 1 ] , dp[ i - 1 ] [ 0 ] - prices[ i] ) ;
dp[ i] [ 2 ] = Math . max ( dp[ i - 1 ] [ 2 ] , dp[ i - 1 ] [ 1 ] + prices[ i] ) ;
dp[ i] [ 3 ] = Math . max ( dp[ i - 1 ] [ 3 ] , dp[ i - 1 ] [ 2 ] - prices[ i] ) ;
dp[ i] [ 4 ] = Math . max ( dp[ i - 1 ] [ 4 ] , dp[ i - 1 ] [ 3 ] + prices[ i] ) ;
}
return dp[ prices. length - 1 ] [ 4 ] ;
}
}
class Solution {
public int maxProfit ( int [ ] prices) {
int [ ] [ ] dp = new int [ 2 ] [ 5 ] ;
dp[ 0 ] [ 0 ] = 0 ;
dp[ 0 ] [ 1 ] = - prices[ 0 ] ;
dp[ 0 ] [ 2 ] = 0 ;
dp[ 0 ] [ 3 ] = - prices[ 0 ] ;
dp[ 0 ] [ 4 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ 1 ] [ 1 ] = Math . max ( dp[ 0 ] [ 1 ] , dp[ 0 ] [ 0 ] - prices[ i] ) ;
dp[ 1 ] [ 2 ] = Math . max ( dp[ 0 ] [ 2 ] , dp[ 0 ] [ 1 ] + prices[ i] ) ;
dp[ 1 ] [ 3 ] = Math . max ( dp[ 0 ] [ 3 ] , dp[ 0 ] [ 2 ] - prices[ i] ) ;
dp[ 1 ] [ 4 ] = Math . max ( dp[ 0 ] [ 4 ] , dp[ 0 ] [ 3 ] + prices[ i] ) ;
dp[ 0 ] [ 1 ] = dp[ 1 ] [ 1 ] ;
dp[ 0 ] [ 2 ] = dp[ 1 ] [ 2 ] ;
dp[ 0 ] [ 3 ] = dp[ 1 ] [ 3 ] ;
dp[ 0 ] [ 4 ] = dp[ 1 ] [ 4 ] ;
}
return dp[ 1 ] [ 4 ] ;
}
}
188 买卖股票的最佳时机IV
同上123 买卖股票的最佳时机III,状态为2k+1种(奇数为买入,偶数为卖出)。 DP:
定义dp数组dp[i][j]表示第i天状态j得到的最大利润; 递推公式dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]),dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]); 初始化奇数为-prices[0],偶数为0; 遍历顺序正序; 举例。 补充:可以使用滚动数组优化。 DP优化:
class Solution {
public int maxProfit ( int k, int [ ] prices) {
if ( prices. length <= 1 ) return 0 ;
int [ ] [ ] dp = new int [ 2 ] [ 2 * k + 1 ] ;
for ( int i = 1 ; i < 2 * k + 1 ; i += 2 ) {
dp[ 0 ] [ i] = - prices[ 0 ] ;
}
for ( int i = 1 ; i < prices. length; i++ ) {
for ( int j = 0 ; j <= 2 * k - 1 ; j += 2 ) {
dp[ 1 ] [ j + 1 ] = Math . max ( dp[ 0 ] [ j + 1 ] , dp[ 0 ] [ j] - prices[ i] ) ;
dp[ 1 ] [ j + 2 ] = Math . max ( dp[ 0 ] [ j + 2 ] , dp[ 0 ] [ j + 1 ] + prices[ i] ) ;
dp[ 0 ] [ j + 1 ] = dp[ 1 ] [ j + 1 ] ;
dp[ 0 ] [ j + 2 ] = dp[ 1 ] [ j + 2 ] ;
}
}
return dp[ 1 ] [ 2 * k] ;
}
}
309 买卖股票的最佳时机含冷冻期
对比上面的122 买卖股票的最佳时机II,同样可以买卖多次,但是卖出后有一天冷冻期不能买入,因此当天有四种状态(买入,两天前卖出,当天卖出,冷冻期)。 DP:
定义dp数组dp[i][j]表示第i天状态j得到的最大利润; 递推公式dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]),dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]),dp[i][2] = dp[i - 1][0] + prices[i],dp[i][3] = dp[i - 1][2]; 初始化dp[0][0] = -prices[0],dp[0][1] = 0,dp[0][2] = 0,dp[0][3] = 0,最后结果是取状态二、状态三和状态四的最大值; 遍历顺序正序; 举例。 补充:可以使用滚动数组优化。 DP优化:
class Solution {
public int maxProfit ( int [ ] prices) {
if ( prices. length == 1 ) return 0 ;
int [ ] [ ] dp = new int [ 2 ] [ 4 ] ;
dp[ 0 ] [ 0 ] = - prices[ 0 ] ;
dp[ 0 ] [ 1 ] = 0 ;
dp[ 0 ] [ 2 ] = 0 ;
dp[ 0 ] [ 3 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ 1 ] [ 0 ] = Math . max ( dp[ 0 ] [ 0 ] , Math . max ( dp[ 0 ] [ 1 ] - prices[ i] , dp[ 0 ] [ 3 ] - prices[ i] ) ) ;
dp[ 1 ] [ 1 ] = Math . max ( dp[ 0 ] [ 1 ] , dp[ 0 ] [ 3 ] ) ;
dp[ 1 ] [ 2 ] = dp[ 0 ] [ 0 ] + prices[ i] ;
dp[ 1 ] [ 3 ] = dp[ 0 ] [ 2 ] ;
dp[ 0 ] [ 0 ] = dp[ 1 ] [ 0 ] ;
dp[ 0 ] [ 1 ] = dp[ 1 ] [ 1 ] ;
dp[ 0 ] [ 2 ] = dp[ 1 ] [ 2 ] ;
dp[ 0 ] [ 3 ] = dp[ 1 ] [ 3 ] ;
}
return Math . max ( dp[ 1 ] [ 1 ] , Math . max ( dp[ 1 ] [ 2 ] , dp[ 1 ] [ 3 ] ) ) ;
}
}
714 买卖股票的最佳时机含手续费
贪心之前实现过 。对比上面的122 买卖股票的最佳时机II,同样可以买卖多次,但是每次买卖都需要手续费,因此计算利润时需要扣除手续费,卖出股票时递推公式修改为dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee)。 DP优化:
class Solution {
public int maxProfit ( int [ ] prices, int fee) {
if ( prices. length == 1 ) return 0 ;
int [ ] [ ] dp = new int [ 2 ] [ 2 ] ;
dp[ 0 ] [ 0 ] = - prices[ 0 ] ;
dp[ 0 ] [ 1 ] = 0 ;
for ( int i = 1 ; i < prices. length; i++ ) {
dp[ 1 ] [ 0 ] = Math . max ( dp[ 0 ] [ 0 ] , dp[ 0 ] [ 1 ] - prices[ i] ) ;
dp[ 1 ] [ 1 ] = Math . max ( dp[ 0 ] [ 1 ] , dp[ 0 ] [ 0 ] + prices[ i] - fee) ;
dp[ 0 ] [ 0 ] = dp[ 1 ] [ 0 ] ;
dp[ 0 ] [ 1 ] = dp[ 1 ] [ 1 ] ;
}
return dp[ 1 ] [ 1 ] ;
}
}