目录
1. 按摩师
1.1 算法原理
- 状态表示:
以i位置为结尾,到达i位置的最长预约时间数。此时dp[i]又分为选择i位置和不选i位置:
1. f[i]:选择i位置
2. g[i]:不选i位置
f[i]=g[i-1]+arr[i];//选了当前位置,就不能选前一个位置
g[i]=max(f[i-1],g[i-1]);//不选当前位置,前一个位置也可选可不选(挑最大值)
- 初始化:
f[0]=arr[0];
g[0]=0;
- 建表顺序:
从左往右
- 返回值:
max(f[n-1], g[n-1]);
1.2 算法代码
class Solution {
public int massage(int[] nums) {
int n = nums.length;
if(n == 0) return 0;
int[] f = new int[n];//选当前位置
int[] g = new int[n];//不选当前位置
f[0] = nums[0];
g[0] = 0;
for(int i = 1; i < n; i++) {
f[i] = g[i - 1] + nums[i];
g[i] = Math.max(f[i - 1], g[i - 1]);
}
return Math.max(g[n - 1], f[n - 1]);
}
}
2、打家劫舍 II
2.1 算法原理
分情况:
- nums[0]偷 --> nums[n-1]不能偷 --> [2,n-2]
- nums[0]不偷 --> [1,n-1](正常打家劫舍)
- 状态表示:
以i位置为结尾,偷到该位置的最大金额
1.f[i]:该位置偷
2.g[i]:该位置不偷
- 状态转移方程:
f[i]=nums[i]+g[i-1]; g[i]=max(g[i-1],f[i-1]);
- 初始化:
f[left] = nums[left]; g[left] = 0;
- 返回值:
max(f[right],g[right]);
2.2 算法代码
class Solution {
public int rob(int[] nums) {
int n = nums.length;
//分情况:1. 第一个位置填 2. 第一个位置不填
return Math.max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));
}
public int rob1(int[] nums, int left, int right) {
if(left > right) return 0;
int n = nums.length;
int[] f = new int[n];//该位置填
int[] g = new int[n];//该位置不填
f[left] = nums[left];
for (int i = left; i <= right; i++) {
f[i] = nums[i] + g[i - 1];
g[i] = Math.max(f[i - 1], g[i - 1]);
}
return Math.max(f[right], g[right]);
}
}
3、删除并获得点数
3.1 算法原理
选择值为x的点数后,则 x+1 / x-1 数值将被删除,也就是说不能增加值为 x+1 / x-1 的点数,故可转化“打家劫舍”问题。
- 状态表示:
到达i位置时,能获得的最大点数。
f[i]:到达i位置,arr[i]必选。 g[i]:到达i位置,arr[i]不选。
- 状态转移方程:
f[i]=arr[i]+g[i-1]; g[i]=max(g[i-1],f[i-1]);
- 初始化:
f[0]=arr[0];
- 返回值:
max(f[MAX_V],g[MAX_V]);
- 预处理arr:
将nums中的数,统计到arr中,然后在arr中进行一次“打家劫舍”。(相邻的数不选,转化为“打家劫舍”问题)
3.2 算法代码
class Solution {
public int deleteAndEarn(int[] nums) {
Arrays.sort(nums);
int maxVal = nums[nums.length - 1];
//arr[i]表示i这个数的总和
int[] arr = new int[maxVal + 1];
//预处理
for(int x : nums) arr[x] += x;
int[] f = new int[maxVal + 1];
int[] g = new int[maxVal + 1];
//初始化
f[0] = arr[0];
//dp
for(int i = 1; i < maxVal + 1; i++) {
f[i] = arr[i] + g[i - 1];
g[i] = Math.max(f[i - 1], g[i - 1]);
}
return Math.max(f[maxVal], g[maxVal]);
}
}
4、粉刷房子
4.1 算法原理
- 状态表示:
dp[i][0]:粉刷到第i个房子时,第i个房子刷红色,此时的最小花费
dp[i][1]:粉刷到第i个房子时,第i个房子刷蓝色,此时的最小花费
dp[i][2]:粉刷到第i个房子时,第i个房子刷绿色,此时的最小花费
- 状态转移方程:
dp[i][0]=min(dp[i-1][1],dp[i-1][2])+costs[i-1][0];
dp[i][1]=min(dp[i-1][0],dp[i-1][2])+costs[i-1][1];
dp[i][2]=min(dp[i-1][0],dp[i-1][1])+costs[i-1][2];
- 初始化:多开辟一行.
下标映射:dp[i][x]-->costs[i-1][x];
虚拟节点值:dp[0][j]=0;
- 填表顺序:
从上往下
- 返回值:
min(dp[n][0],dp[n][1],dp[n][2]);
4.2 算法代码
class Solution {
public int minCost1(int[][] costs) {
int n = costs.length;
int[] f = new int[n + 1];// 红
int[] g = new int[n + 1];// 蓝
int[] h = new int[n + 1];// 绿
for (int i = 1; i <= n; i++) {
f[i] = Math.min(g[i - 1], h[i - 1]) + costs[i - 1][0];
g[i] = Math.min(f[i - 1], h[i - 1]) + costs[i - 1][1];
h[i] = Math.min(g[i - 1], f[i - 1]) + costs[i - 1][2];
}
return Math.min(Math.min(f[n], g[n]), h[n]);
}
public int minCost(int[][] costs) {
int n = costs.length;
int[][] dp = new int[n + 1][3];
for (int i = 1; i <= n; i++) {
dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0];
dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1];
dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2];
}
return Math.min(Math.min(dp[n][0], dp[n][1]), dp[n][2]);
}
}
5、买卖股票的最佳时机含冷冻期
5.1 算法原理
- 状态表示:
dp[i][0]:第i天后,处于“买入”状态,此时的最大利润
dp[i][1]:第i天后,处于“可交易”状态,此时的最大利润(“冷冻期”过后的可交易状态)
dp[i][2]:第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 - 1][2]);// 可交易
dp[i][2] = dp[i - 1][0] + prices[i];// 冷冻期
- 初始化:
dp[0][0]=-prices[0];
dp[0][1]=0;
dp[0][2]=0;
- 填表顺序:
从上到下
- 返回值:
max(dp[n][1],dp[n][2]);
5.2 算法代码
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][3];
dp[0][0] = -prices[0]; dp[0][1] = 0; dp[0][2] = 0;
for(int i = 1; i < n; 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 - 1][2]);// 可交易
dp[i][2] = dp[i - 1][0] + prices[i];// 冷冻期
}
return Math.max(dp[n - 1][1], dp[n - 1][2]);
}
}
6、买卖股票的最佳时机含手续费
6.1 算法原理
- 状态表示:
f[i]:第i天结束后,处于"买入"状态,此时的最大利润。
g[i]:第i天结束后,处于"卖出"状态,此时的最大利润。
- 状态转移方程:
f[i]=max(f[i-1], g[i-1]-prices[i]);
g[i]=max(f[i-1]+prices[i]-fee,g[i-1]);
- 初始化:
f[0]=-prices[0];
g[0]=0;
- 填表顺序:
从左往右,两个表一起填
- 返回值:
g[n-1]
6.2 算法代码
class Solution {
public int maxProfit(int[] prices, int fee) {
int n = prices.length;
int[] f = new int[n];// 该天结束后,处于买入状态,最大利润
int[] g = new int[n];// 该天结束后,处于卖出状态,最大利润
f[0] = -prices[0]; g[0] = 0;
for(int i = 1; i < n; i++) {
f[i] = Math.max(f[i - 1], g[i - 1] - prices[i]);
g[i] = Math.max(f[i - 1] + prices[i] - fee, g[i - 1]);
}
return g[n - 1];
}
}
7、买卖股票的最佳时机 III 【hard】
7.1 算法原理
- 状态表示:
f[i][j]:第i天结束后,完成了j次交易,处于"买入"状态,利润最大值
g[i][j]:第i天结束后,完成了j次交易,处于"卖出"状态,利润最大值
- 状态转移方程:
f[i][j]=max(f[i-1][j], g[i-1][j]-p[i]);
g[i][j]=g[i-1][j];
if(j-1 >= 0) --> g[i][j]=max(g[i][j], f[i-1][j-1]+p[i]);
- 初始化:
f[0][0]=-p[0];// 第0天,买入
g[0][0]=0;// 卖出的可交易状态,利润为0
f[0][1/2]=g[0][1/2]=min;// 交易次数是有限的,要尽可能减小交易次数
- 建表顺序:
从上到下,从左到右,两个表一起填
- 返回值
g表最后一行中的最大值
注意,进行加减操作时,防溢出:
- 正无穷表示:0x3f3f3f3f
- 负无穷表示:-0x3f3f3f3f
7.2 算法代码
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] f = new int[n][3];
int[][] g = new int[n][3];
// -0x3f3f3f3f 表示无穷小
// 初始化
for(int i = 0; i < 3; i++) f[0][i] = g[0][i] = -0x3f3f3f3f;
f[0][0] = -prices[0];
g[0][0] = 0;
// 填表
for(int i = 1; i < n; i++) {
for(int j = 0; j < 3; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if(j - 1 >= 0) g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
int ret = 0;
for(int x : g[n - 1]) ret = Math.max(ret, x);
return ret;
}
}
8、买卖股票的最佳时机 IV
8.1 算法原理
题7解法采用的是通用解法,本题解法与题7完全一致。
8.2 算法代码
class Solution {
public int maxProfit(int k, int[] prices) {
int n = prices.length;
k = Math.min(k, n / 2);
int[][] f = new int[n][k + 1];
int[][] g = new int[n][k + 1];
// -0x3f3f3f3f 表示无穷小
// 初始化
for(int i = 0; i <= k; i++) f[0][i] = g[0][i] = -0x3f3f3f3f;
f[0][0] = -prices[0];
g[0][0] = 0;
// 填表
for(int i = 1; i < n; i++) {
for(int j = 0; j <= k; j++) {
f[i][j] = Math.max(f[i - 1][j], g[i - 1][j] - prices[i]);
g[i][j] = g[i - 1][j];
if(j - 1 >= 0) g[i][j] = Math.max(g[i][j], f[i - 1][j - 1] + prices[i]);
}
}
int ret = 0;
for(int x : g[n - 1]) ret = Math.max(ret, x);
return ret;
}
}
END