一、leetcode题目
1.买卖股票的最佳时机 III
题目描述
给定一个数组,它的第 i
个元素是一支给定的股票在第 i
天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
2.测试用例
输入:
prices = [3,3,5,0,0,3,1,4]
输出:
6
解释:
在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
提示
- 1 < = p r i c e s . l e n g t h < = 1 0 5 1 <= prices.length <= 10^5 1<=prices.length<=105
- 0 < = p r i c e s [ i ] < = 1 0 5 0 <= prices[i] <= 10^5 0<=prices[i]<=105
3.思路
还是动态规划的树形dp
,不过本题有较多限制,限制股票买卖次数,只能够买卖两次股票。这就需要有多种状态表示。
- 状态表示:
- dp[i][0] 为不购买股票的最大价值。
- dp[i][1] 为第一次购买股票能够达到的最大价值。
- dp[i][2] 为第一次卖出股票能够达到的最大价值。
- dp[i][3] 为第二次买入股票能够达到的最大价值。
- dp[i][4] 为第二次卖出股票能够达到的最大价值。
- 状态转移方程
- dp[i][0] = dp[i-1][0];
- dp[i][1] = max(dp[i-1][1], dp[i-1][0] - prices[i]);
- dp[i][2] = max(dp[i-1][2], dp[i-1][1] - prices[i]);
- 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][1] = -prices[0], dp[0][3] = -prices[0];(因为同一天可以执行一次买入卖出操作,所以第一天的第二次买入的最大价值也为-prices[0]).
4.算法实现
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][5];
// dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
for(int i = 1; i < n; i++){
dp[i][0] = dp[i-1][0];
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[n-1][4];
}
}
因为dp[i]阶段的状态只依赖dp[i-1]阶段的状态,可只维护上个阶段的所有状态进行状态压缩。
下面是状态压缩代码的代码实现:
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] dp = new int[5];
dp[1] = -prices[0];
dp[3] = -prices[0];
for(int i = 1; i < n; i++){
dp[1] = Math.max(dp[1], dp[0] - prices[i]);
dp[2] = Math.max(dp[2], dp[1] + prices[i]);
dp[3] = Math.max(dp[3], dp[2] - prices[i]);
dp[4] = Math.max(dp[4], dp[3] + prices[i]);
}
return dp[4];
}
}
二、蓝桥题目
1.排列数
题目描述
在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。
对于一个 1 ~ n 1 ~ n 1~n 的排列,如果可以将这个排列中包含 t t t 个折点,则它称为一个 t + 1 t + 1 t+1 单调序列。
例如,排列 (1,4,2,3)
是一个 3
单调序列,其中 4
和 2
都是折点。
给定
n
n
n 和
k
k
k,请问
1
n
1 ~ n
1 n 的所有排列中有多少个
k
k
k 单调队列?
输入描述
输入一行包含两个整数
n
,
k
(
1
≤
k
≤
n
≤
500
)
n, k\ (1 \leq k \leq n \leq 500)
n,k (1≤k≤n≤500)。
输出描述
输出一个整数,表示答案。答案可能很大,你可需要输出满足条件的排列数量除以 123456 的余数即可。
2.测试用例
输入:
4 2
输出:
12
3.思路
是动态规划,因为这一阶段的值可由上一阶段推导而来,但推导不出具体的状态转移方程式,就借鉴了一下题解。
4.算法实现
import java.util.Scanner;
public class main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int K = sc.nextInt();
sc.close();
int[][] dp = new int[N+1][K+1];
dp[1][1]=1;
dp[2][1]=2;
for(int i=3;i<=N;i++){
for(int j=1;j<=i&&j<=K;j++){
dp[i][j] += dp[i - 1][j] * j;
dp[i][j]%=123456;
dp[i][j] += dp[i - 1][j - 1] * 2;
dp[i][j]%=123456;
if (j > 1){
dp[i][j] += dp[i - 1][j - 2] * (i - j);
dp[i][j]%=123456;
}
}
}
System.out.println(dp[N][K]);
}
}