简单多状态 dp 问题(典型算法思想)—— OJ例题算法解析思路
目录
一、面试题 17.16. 按摩师 - 力扣(LeetCode)
算法代码:
代码思路解析:
问题分析:
动态规划定义:
状态转移方程:
初始化:
填表:
返回值:
优化空间复杂度:
二、213. 打家劫舍 II - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 主函数 rob:
2. 辅助函数 rob1:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
三、740. 删除并获得点数 - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 预处理:
2. 转化为“打家劫舍”问题:
3. 动态规划定义:
4. 状态转移方程:
5. 初始化:
6. 填表:
7. 返回结果:
优化空间复杂度:
总结:
四、 LCR 091. 粉刷房子 - 力扣(LeetCode)
方法一:算法代码(dp从1开始,costs从0开始)
索引对齐问题:
代码思路解析:
1. 问题分析:
2. 动态规划定义:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
方法二:算法代码(dp从0开始,costs也从0开始)
1. 从 0 开始的动态规划表:
2. 状态转移方程:
3. 初始化:
4. 为什么可以从 0 开始?
5. 举例说明:
6. 总结:
五、309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 问题分析:
2. 动态规划定义:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
六、 714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 问题分析:
2. 动态规划定义:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
七、123. 买卖股票的最佳时机 III - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 问题分析:
2. 动态规划定义:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
八、 188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
算法代码:
代码思路解析:
1. 问题分析:
2. 动态规划定义:
3. 状态转移方程:
4. 初始化:
5. 填表:
6. 返回结果:
优化空间复杂度:
总结:
一、面试题 17.16. 按摩师 - 力扣(LeetCode)
算法代码:
class Solution {
public:int massage(vector<int>& nums) {int n = nums.size();if (n == 0)return 0; // 处理边界条件// 定义两个状态数组vector<int> f(n); // f[i] 表示选择第 i 个预约时的最大价值vector<int> g(n); // g[i] 表示不选择第 i 个预约时的最大价值// 初始化f[0] = nums[0];g[0] = 0;// 填表for (int i = 1; i < n; i++) {f[i] = g[i - 1] + nums[i]; // 选择第 i 个预约g[i] = max(f[i - 1], g[i - 1]); // 不选择第 i 个预约}// 返回最大值return max(f[n - 1], g[n - 1]);}
};
这个代码实现了一个动态规划问题,通常被称为“按摩师问题”或“打家劫舍问题”。问题的核心是:给定一个数组 nums
,表示每个预约的时长或价值,要求在不连续选择相邻元素的情况下,找到最大价值的组合。
代码思路解析:
-
问题分析:
-
每个元素
nums[i]
表示第i
个预约的价值。 -
不能选择相邻的预约,即如果选择了
nums[i]
,就不能选择nums[i-1]
和nums[i+1]
。 -
目标是找到一种选择方式,使得总价值最大。
-
-
动态规划定义:
-
定义两个状态数组
f
和g
:-
f[i]
表示选择第i
个预约时的最大价值。 -
g[i]
表示不选择第i
个预约时的最大价值。
-
-
通过这两个状态数组,可以递推地计算出每一步的最优解。
-
-
状态转移方程:
-
如果选择第
i
个预约,那么前一个预约不能选择,因此f[i] = g[i-1] + nums[i]
。 -
如果不选择第
i
个预约,那么前一个预约可以选择或不选择,取两者中的最大值,因此g[i] = max(f[i-1], g[i-1])
。
-
-
初始化:
-
f[0] = nums[0]
:如果只有一个预约,那么选择它的价值就是nums[0]
。 -
g[0] = 0
:如果不选择第一个预约,那么价值为 0。
-
-
填表:
-
从
i = 1
开始,逐步计算f[i]
和g[i]
,直到i = n-1
。
-
-
返回值:
-
最终的结果是
max(f[n-1], g[n-1])
,即选择或不选择最后一个预约的最大价值。
-
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int massage(vector<int>& nums) {int n = nums.size();if (n == 0)return 0;int f = nums[0]; // 选择当前预约的最大价值int g = 0; // 不选择当前预约的最大价值for (int i = 1; i < n; i++) {int new_f = g + nums[i]; // 选择当前预约int new_g = max(f, g); // 不选择当前预约f = new_f;g = new_g;}return max(f, g);}
};
这个优化版本只使用了两个变量 f
和 g
,空间复杂度降低到了 O(1)
。
二、213. 打家劫舍 II - 力扣(LeetCode)
算法代码:
class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();if (n == 0) return 0;if (n == 1) return nums[0]; // 边界条件处理// 两种情况下的最大值return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));}int rob1(vector<int>& nums, int left, int right) {if (left > right) return 0; // 边界条件处理int n = nums.size();vector<int> f(n); // f[i] 表示偷第 i 个房屋时的最大价值vector<int> g(n); // g[i] 表示不偷第 i 个房屋时的最大价值// 初始化f[left] = nums[left];g[left] = 0;// 填表for (int i = left + 1; i <= right; i++) {f[i] = g[i - 1] + nums[i]; // 偷第 i 个房屋g[i] = max(f[i - 1], g[i - 1]); // 不偷第 i 个房屋}// 返回结果return max(f[right], g[right]);}
};
这个代码实现了一个动态规划问题,通常被称为“环形打家劫舍问题”。与普通的打家劫舍问题不同,这里的房屋是环形排列的,即第一个房屋和最后一个房屋是相邻的。因此,我们需要考虑两种情况:
-
偷第一个房屋,但不能偷最后一个房屋。
-
不偷第一个房屋,可以偷最后一个房屋。
最终的结果是这两种情况的最大值。
代码思路解析:
1. 主函数 rob
:
-
首先检查数组的长度
n
。 -
调用辅助函数
rob1
分别计算两种情况的最大值:-
情况 1:偷第一个房屋,但不能偷最后一个房屋。即
nums[0] + rob1(nums, 2, n - 2)
。 -
情况 2:不偷第一个房屋,可以偷最后一个房屋。即
rob1(nums, 1, n - 1)
。
-
-
返回两种情况的最大值。
2. 辅助函数 rob1
:
-
这个函数用于计算在给定区间
[left, right]
内偷取房屋的最大价值。 -
使用动态规划的思想,定义两个状态数组
f
和g
:-
f[i]
表示偷第i
个房屋时的最大价值。 -
g[i]
表示不偷第i
个房屋时的最大价值。
-
-
通过状态转移方程逐步计算每个位置的最优解。
3. 状态转移方程:
-
如果偷第
i
个房屋,那么前一个房屋不能偷,因此f[i] = g[i - 1] + nums[i]
。 -
如果不偷第
i
个房屋,那么前一个房屋可以偷或不偷,取两者中的最大值,因此g[i] = max(f[i - 1], g[i - 1])
。
4. 初始化:
-
f[left] = nums[left]
:如果从left
开始偷,那么偷第一个房屋的价值就是nums[left]
。 -
g[left] = 0
:如果不偷第一个房屋,那么价值为 0。
5. 填表:
-
从
left + 1
开始,逐步计算f[i]
和g[i]
,直到i = right
。
6. 返回结果:
-
最终的结果是
max(f[right], g[right])
,即偷或不偷最后一个房屋的最大价值。
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();if (n == 0) return 0;if (n == 1) return nums[0]; // 边界条件处理// 两种情况下的最大值return max(nums[0] + rob1(nums, 2, n - 2), rob1(nums, 1, n - 1));}int rob1(vector<int>& nums, int left, int right) {if (left > right) return 0; // 边界条件处理int f = nums[left]; // 偷当前房屋的最大价值int g = 0; // 不偷当前房屋的最大价值for (int i = left + 1; i <= right; i++) {int new_f = g + nums[i]; // 偷当前房屋int new_g = max(f, g); // 不偷当前房屋f = new_f;g = new_g;}return max(f, g);}
};
总结:
-
主函数
rob
通过调用辅助函数rob1
分别计算两种情况的最大值。 -
辅助函数
rob1
使用动态规划的思想,通过状态转移方程计算区间[left, right]
内的最大价值。 -
通过滚动数组优化,空间复杂度从
O(n)
降低到O(1)
。
三、740. 删除并获得点数 - 力扣(LeetCode)
算法代码:
class Solution {
public:int deleteAndEarn(vector<int>& nums) {const int N = 10001;// 1. 预处理int arr[N] = {0};for (auto x : nums)arr[x] += x;// 2. 在 arr 数组上,做一次“打家劫舍”问题// 创建 dp 表vector<int> f(N); // f[i] 表示选择第 i 个元素时的最大点数vector<int> g(N); // g[i] 表示不选择第 i 个元素时的最大点数// 填表for (int i = 1; i < N; i++) {f[i] = g[i - 1] + arr[i]; // 选择第 i 个元素g[i] = max(f[i - 1], g[i - 1]); // 不选择第 i 个元素}// 返回结果return max(f[N - 1], g[N - 1]);}
};
这个代码实现了一个动态规划问题,通常被称为“删除并获得点数”问题。问题的核心是:给定一个整数数组 nums
,你可以通过删除一个元素 nums[i]
来获得 nums[i]
的点数,但同时必须删除所有等于 nums[i] - 1
和 nums[i] + 1
的元素。目标是最大化获得的点数。
代码思路解析:
1. 预处理:
-
由于数组中的元素可能很大(最大为
10000
),我们使用一个大小为10001
的数组arr
来统计每个数字的总点数。 -
遍历
nums
,将每个数字x
的点数累加到arr[x]
中。例如,如果nums = [2, 2, 3, 3, 3]
,那么arr[2] = 4
,arr[3] = 9
。
2. 转化为“打家劫舍”问题:
-
经过预处理后,问题转化为在
arr
数组上解决“打家劫舍”问题:-
不能选择相邻的元素(因为选择
arr[i]
意味着不能选择arr[i-1]
和arr[i+1]
)。 -
目标是最大化选择的元素的总和。
-
3. 动态规划定义:
-
定义两个状态数组
f
和g
:-
f[i]
表示选择第i
个元素时的最大点数。 -
g[i]
表示不选择第i
个元素时的最大点数。
-
-
通过这两个状态数组,可以递推地计算出每一步的最优解。
4. 状态转移方程:
-
如果选择第
i
个元素,那么前一个元素不能选择,因此f[i] = g[i - 1] + arr[i]
。 -
如果不选择第
i
个元素,那么前一个元素可以选择或不选择,取两者中的最大值,因此g[i] = max(f[i - 1], g[i - 1])
。
5. 初始化:
-
f[0] = 0
和g[0] = 0
,因为没有元素时点数为 0。
6. 填表:
-
从
i = 1
开始,逐步计算f[i]
和g[i]
,直到i = N - 1
。
7. 返回结果:
-
最终的结果是
max(f[N - 1], g[N - 1])
,即选择或不选择最后一个元素的最大点数。
优化空间复杂度:
当前的空间复杂度是 O(N)
,其中 N = 10001
。可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int deleteAndEarn(vector<int>& nums) {const int N = 10001;// 1. 预处理int arr[N] = {0};for (auto x : nums)arr[x] += x;// 2. 在 arr 数组上,做一次“打家劫舍”问题int f = 0; // 选择当前元素的最大点数int g = 0; // 不选择当前元素的最大点数for (int i = 1; i < N; i++) {int new_f = g + arr[i]; // 选择当前元素int new_g = max(f, g); // 不选择当前元素f = new_f;g = new_g;}// 返回结果return max(f, g);}
};
总结:
-
通过预处理将问题转化为“打家劫舍”问题。
-
使用动态规划的思想,通过状态转移方程计算最大点数。
-
通过滚动数组优化,空间复杂度从
O(N)
降低到O(1)
。
四、 LCR 091. 粉刷房子 - 力扣(LeetCode)
方法一:算法代码(dp从1开始,costs从0开始)
class Solution {
public:int minCost(vector<vector<int>>& costs) {int n = costs.size();// dp[i][j] 表示将第 i 个房子粉刷成第 j 种颜色时的最小总成本vector<vector<int>> dp(n + 1, vector<int>(3, 0));for (int i = 1; i <= n; i++) {// 选择颜色 0dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i - 1][0];// 选择颜色 1dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i - 1][1];// 选择颜色 2dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i - 1][2];}// 返回最后一个房子选择三种颜色中的最小总成本return min(dp[n][0], min(dp[n][1], dp[n][2]));}
};
索引对齐问题:
-
动态规划表
dp
的索引从1
开始:-
dp[i][j]
表示第i
个房子粉刷成第j
种颜色的最小成本。 -
i
的范围是1
到n
,表示第1
个房子到第n
个房子。
-
-
输入数组
costs
的索引从0
开始:-
costs[k][j]
表示第k
个房子粉刷成第j
种颜色的成本。 -
k
的范围是0
到n-1
,表示第1
个房子到第n
个房子。
-
因此,dp
表的第 i
个房子对应 costs
的第 i-1
个房子。
这个代码实现了一个动态规划问题,通常被称为“粉刷房子”问题。问题的核心是:给定一个 n x 3
的二维数组 costs
,其中 costs[i][j]
表示将第 i
个房子粉刷成第 j
种颜色的成本。要求相邻的房子不能粉刷成相同的颜色,求最小总成本。
代码思路解析:
1. 问题分析:
-
每个房子有三种颜色可选(假设为红、蓝、绿)。
-
相邻的房子不能选择相同的颜色。
-
目标是找到一种粉刷方案,使得总成本最小。
2. 动态规划定义:
-
定义
dp[i][j]
表示将第i
个房子粉刷成第j
种颜色时的最小总成本。 -
j
的取值范围是0
、1
、2
,分别对应三种颜色。
3. 状态转移方程:
-
对于第
i
个房子,如果选择颜色0
,那么第i-1
个房子只能选择颜色1
或2
。因此:dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[i-1][0];
-
如果选择颜色
1
,那么第i-1
个房子只能选择颜色0
或2
。因此:dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[i-1][1];
-
如果选择颜色
2
,那么第i-1
个房子只能选择颜色0
或1
。因此:dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[i-1][2];
4. 初始化:
-
dp[0][0] = dp[0][1] = dp[0][2] = 0
,表示没有房子时的成本为 0。
5. 填表:
-
从
i = 1
开始,逐步计算dp[i][0]
、dp[i][1]
和dp[i][2]
,直到i = n
。
6. 返回结果:
-
最终的结果是
min(dp[n][0], dp[n][1], dp[n][2])
,即最后一个房子选择三种颜色中的最小总成本。
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int minCost(vector<vector<int>>& costs) {int n = costs.size();// 使用滚动数组优化空间复杂度int dp0 = 0, dp1 = 0, dp2 = 0; // 分别表示选择颜色 0、1、2 的最小成本for (int i = 1; i <= n; i++) {int new_dp0 = min(dp1, dp2) + costs[i - 1][0]; // 选择颜色 0int new_dp1 = min(dp0, dp2) + costs[i - 1][1]; // 选择颜色 1int new_dp2 = min(dp0, dp1) + costs[i - 1][2]; // 选择颜色 2dp0 = new_dp0;dp1 = new_dp1;dp2 = new_dp2;}// 返回最后一个房子选择三种颜色中的最小总成本return min(dp0, min(dp1, dp2));}
};
总结:
-
使用动态规划的思想,通过状态转移方程计算每个房子选择不同颜色的最小成本。
-
通过滚动数组优化,空间复杂度从
O(n)
降低到O(1)
。 -
最终结果是最后一个房子选择三种颜色中的最小总成本。
方法二:算法代码(dp从0开始,costs也从0开始)
class Solution {
public:int minCost(vector<vector<int>>& costs) {int n = costs.size();if (n == 0) return 0; // 边界条件处理// dp[i][j] 表示将第 i 个房子粉刷成第 j 种颜色时的最小总成本vector<vector<int>> dp(n, vector<int>(3));// 初始化第 0 个房子dp[0][0] = costs[0][0];dp[0][1] = costs[0][1];dp[0][2] = costs[0][2];// 填表for (int i = 1; i < n; i++) {dp[i][0] = min(dp[i - 1][1], dp[i - 1][2]) + costs[i][0]; // 选择颜色 0dp[i][1] = min(dp[i - 1][0], dp[i - 1][2]) + costs[i][1]; // 选择颜色 1dp[i][2] = min(dp[i - 1][0], dp[i - 1][1]) + costs[i][2]; // 选择颜色 2}// 返回最后一个房子选择三种颜色中的最小总成本return min(dp[n - 1][0], min(dp[n - 1][1], dp[n - 1][2]));}
};
1. 从 0
开始的动态规划表:
-
dp[i][j]
表示第i
个房子(索引从0
开始)粉刷成第j
种颜色的最小成本。 -
输入数组
costs
的索引也是从0
开始,因此dp[i][j]
直接对应costs[i][j]
。
2. 状态转移方程:
-
对于第
i
个房子(i >= 1
):-
如果选择颜色
0
,则前一个房子不能选择颜色0
:dp[i][0] = min(dp[i-1][1], dp[i-1][2]) + costs[i][0];
-
如果选择颜色
1
,则前一个房子不能选择颜色1
:dp[i][1] = min(dp[i-1][0], dp[i-1][2]) + costs[i][1];
-
如果选择颜色
2
,则前一个房子不能选择颜色2
:dp[i][2] = min(dp[i-1][0], dp[i-1][1]) + costs[i][2];
-
3. 初始化:
-
对于第
0
个房子(i = 0
):-
dp[0][0] = costs[0][0]
:选择颜色0
的成本。 -
dp[0][1] = costs[0][1]
:选择颜色1
的成本。 -
dp[0][2] = costs[0][2]
:选择颜色2
的成本。
-
4. 为什么可以从 0
开始?
-
从
0
开始的设计更符合数组的索引习惯,避免了i - 1
的偏移。 -
初始化时直接使用
costs[0][j]
,逻辑更直观。 -
动态规划表的索引与输入数组的索引完全一致,减少了出错的可能性。
5. 举例说明:
假设输入 costs = [[17, 2, 17], [16, 16, 5], [14, 3, 19]]
,表示有 3 个房子:
-
costs[0] = [17, 2, 17]
:第 0 个房子的三种颜色成本。 -
costs[1] = [16, 16, 5]
:第 1 个房子的三种颜色成本。 -
costs[2] = [14, 3, 19]
:第 2 个房子的三种颜色成本。
动态规划表的计算过程:
房子 (i ) | dp[i][0] (颜色 0) | dp[i][1] (颜色 1) | dp[i][2] (颜色 2) |
---|---|---|---|
0 | 17 | 2 | 17 |
1 | 18 (2 + 16 ) | 33 (17 + 16 ) | 7 (2 + 5 ) |
2 | 10 (7 + 3 ) | 21 (18 + 3 ) | 26 (18 + 19 ) |
最终结果是 min(dp[2][0], dp[2][1], dp[2][2]) = min(10, 21, 26) = 10
。
6. 总结:
-
动态规划表可以从
0
开始,只要正确处理索引对齐问题即可。 -
从
0
开始的设计更直观,避免了i - 1
的偏移,逻辑更清晰。 -
两种实现方式(从
1
开始或从0
开始)都是正确的,选择哪种方式取决于个人习惯和代码风格。
五、309. 买卖股票的最佳时机含冷冻期 - 力扣(LeetCode)
算法代码:
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// dp[i][j] 表示第 i 天结束时处于状态 j 的最大利润vector<vector<int>> dp(n, vector<int>(3));// 初始化dp[0][0] = -prices[0]; // 第 0 天买入股票dp[0][1] = 0; // 第 0 天不可能处于冷冻期dp[0][2] = 0; // 第 0 天不持有股票且不处于冷冻期// 填表for (int i = 1; i < n; i++) {dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i]); // 持有股票dp[i][1] = dp[i - 1][0] + prices[i]; // 不持有股票且处于冷冻期dp[i][2] = max(dp[i - 1][2], dp[i - 1][1]); // 不持有股票且不处于冷冻期}// 返回结果return max(dp[n - 1][1], dp[n - 1][2]);}
};
这个代码实现了一个动态规划问题,通常被称为“股票买卖问题(含冷冻期)”。问题的核心是:给定一个股票价格数组 prices
,你可以进行多次交易,但每次卖出股票后需要有一天的冷冻期(即不能立即买入)。目标是最大化利润。
代码思路解析:
1. 问题分析:
-
每天有三种状态:
-
持有股票(
dp[i][0]
):表示第i
天结束时持有股票的最大利润。 -
不持有股票且处于冷冻期(
dp[i][1]
):表示第i
天结束时没有股票,且处于冷冻期的最大利润。 -
不持有股票且不处于冷冻期(
dp[i][2]
):表示第i
天结束时没有股票,且不处于冷冻期的最大利润。
-
-
目标是找到最后一天的最大利润。
2. 动态规划定义:
-
定义
dp[i][j]
表示第i
天结束时处于状态j
的最大利润。 -
j
的取值范围是0
、1
、2
,分别对应上述三种状态。
3. 状态转移方程:
-
持有股票(
dp[i][0]
):-
可能是前一天就持有股票,即
dp[i-1][0]
。 -
也可能是今天买入股票,那么前一天必须是不持有股票且不处于冷冻期,即
dp[i-1][2] - prices[i]
。 -
取两者的最大值:
dp[i][0] = max(dp[i-1][0], dp[i-1][2] - prices[i]);
-
-
不持有股票且处于冷冻期(
dp[i][1]
):-
只有一种可能:今天卖出了股票,即前一天持有股票并卖出,即
dp[i-1][0] + prices[i]
。 -
因此:
dp[i][1] = dp[i-1][0] + prices[i];
-
-
不持有股票且不处于冷冻期(
dp[i][2]
):-
可能是前一天就不持有股票且不处于冷冻期,即
dp[i-1][2]
。 -
也可能是前一天处于冷冻期,即
dp[i-1][1]
。 -
取两者的最大值:
dp[i][2] = max(dp[i-1][2], dp[i-1][1]);
-
4. 初始化:
-
第 0 天(初始状态):
-
dp[0][0] = -prices[0]
:第 0 天买入股票,利润为-prices[0]
。 -
dp[0][1] = 0
:第 0 天不可能处于冷冻期。 -
dp[0][2] = 0
:第 0 天不持有股票且不处于冷冻期。
-
5. 填表:
-
从
i = 1
开始,逐步计算dp[i][0]
、dp[i][1]
和dp[i][2]
,直到i = n-1
。
6. 返回结果:
-
最终的结果是
max(dp[n-1][1], dp[n-1][2])
,即最后一天不持有股票的最大利润(可能是冷冻期或非冷冻期)。
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int maxProfit(vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// 使用滚动数组优化空间复杂度int dp0 = -prices[0]; // 持有股票int dp1 = 0; // 不持有股票且处于冷冻期int dp2 = 0; // 不持有股票且不处于冷冻期for (int i = 1; i < n; i++) {int new_dp0 = max(dp0, dp2 - prices[i]); // 持有股票int new_dp1 = dp0 + prices[i]; // 不持有股票且处于冷冻期int new_dp2 = max(dp2, dp1); // 不持有股票且不处于冷冻期dp0 = new_dp0;dp1 = new_dp1;dp2 = new_dp2;}// 返回结果return max(dp1, dp2);}
};
总结:
-
使用动态规划的思想,通过状态转移方程计算每天的最大利润。
-
通过滚动数组优化,空间复杂度从
O(n)
降低到O(1)
。 -
最终结果是最后一天不持有股票的最大利润(可能是冷冻期或非冷冻期)。
六、 714. 买卖股票的最佳时机含手续费 - 力扣(LeetCode)
算法代码:
class Solution {
public:int maxProfit(vector<int>& prices, int fee) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// f[i] 表示第 i 天结束时持有股票的最大利润// g[i] 表示第 i 天结束时没有股票的最大利润vector<int> f(n);vector<int> g(n);// 初始化f[0] = -prices[0]; // 第 0 天买入股票g[0] = 0; // 第 0 天不持有股票// 填表for (int i = 1; i < n; i++) {f[i] = max(f[i - 1], g[i - 1] - prices[i]); // 持有股票g[i] = max(g[i - 1], f[i - 1] + prices[i] - fee); // 不持有股票}// 返回结果return g[n - 1];}
};
这个代码实现了一个动态规划问题,通常被称为“股票买卖问题(含手续费)”。问题的核心是:给定一个股票价格数组 prices
和一个交易手续费 fee
,你可以进行多次交易,但每次卖出股票时需要支付手续费。目标是最大化利润。
代码思路解析:
1. 问题分析:
-
每天有两种状态:
-
持有股票(
f[i]
):表示第i
天结束时持有股票的最大利润。 -
不持有股票(
g[i]
):表示第i
天结束时没有股票的最大利润。
-
-
目标是找到最后一天的最大利润。
2. 动态规划定义:
-
定义两个状态数组
f
和g
:-
f[i]
表示第i
天结束时持有股票的最大利润。 -
g[i]
表示第i
天结束时没有股票的最大利润。
-
3. 状态转移方程:
-
持有股票(
f[i]
):-
可能是前一天就持有股票,即
f[i-1]
。 -
也可能是今天买入股票,那么前一天必须没有股票,即
g[i-1] - prices[i]
。 -
取两者的最大值:
f[i] = max(f[i-1], g[i-1] - prices[i]);
-
-
不持有股票(
g[i]
):-
可能是前一天就没有股票,即
g[i-1]
。 -
也可能是今天卖出股票,那么前一天必须持有股票,即
f[i-1] + prices[i] - fee
。 -
取两者的最大值:
g[i] = max(g[i-1], f[i-1] + prices[i] - fee);
-
4. 初始化:
-
第 0 天(初始状态):
-
f[0] = -prices[0]
:第 0 天买入股票,利润为-prices[0]
。 -
g[0] = 0
:第 0 天不持有股票,利润为0
。
-
5. 填表:
-
从
i = 1
开始,逐步计算f[i]
和g[i]
,直到i = n-1
。
6. 返回结果:
-
最终的结果是
g[n-1]
,即最后一天不持有股票的最大利润。
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:int maxProfit(vector<int>& prices, int fee) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// 使用滚动数组优化空间复杂度int f = -prices[0]; // 持有股票int g = 0; // 不持有股票for (int i = 1; i < n; i++) {int new_f = max(f, g - prices[i]); // 持有股票int new_g = max(g, f + prices[i] - fee); // 不持有股票f = new_f;g = new_g;}// 返回结果return g;}
};
总结:
-
使用动态规划的思想,通过状态转移方程计算每天的最大利润。
-
通过滚动数组优化,空间复杂度从
O(n)
降低到O(1)
。 -
最终结果是最后一天不持有股票的最大利润。
七、123. 买卖股票的最佳时机 III - 力扣(LeetCode)
算法代码:
class Solution {
public:const int INF = 0x3f3f3f3f; // 表示无穷大int maxProfit(vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// f[i][j] 表示第 i 天结束时,完成了 j 笔交易并持有股票的最大利润// g[i][j] 表示第 i 天结束时,完成了 j 笔交易并不持有股票的最大利润vector<vector<int>> f(n, vector<int>(3, -INF));vector<vector<int>> g(n, vector<int>(3, -INF));// 初始化f[0][0] = -prices[0]; // 第 0 天买入股票g[0][0] = 0; // 第 0 天不持有股票// 填表for (int i = 1; i < n; i++) {for (int j = 0; j < 3; j++) {f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]); // 持有股票g[i][j] = g[i - 1][j]; // 不持有股票if (j >= 1) // 如果该状态存在g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]); // 卖出股票}}// 找到最后一行的最大值int ret = 0;for (int j = 0; j < 3; j++)ret = max(ret, g[n - 1][j]);return ret;}
};
这个代码实现了一个动态规划问题,通常被称为“股票买卖问题(最多完成两笔交易)”。问题的核心是:给定一个股票价格数组 prices
,你最多可以完成两笔交易(买入和卖出算一笔交易),目标是最大化利润。
代码思路解析:
1. 问题分析:
-
每天有两种状态:
-
持有股票(
f[i][j]
):表示第i
天结束时,完成了j
笔交易并持有股票的最大利润。 -
不持有股票(
g[i][j]
):表示第i
天结束时,完成了j
笔交易并不持有股票的最大利润。
-
-
目标是找到最后一天的最大利润。
2. 动态规划定义:
-
定义两个状态数组
f
和g
:-
f[i][j]
表示第i
天结束时,完成了j
笔交易并持有股票的最大利润。 -
g[i][j]
表示第i
天结束时,完成了j
笔交易并不持有股票的最大利润。
-
-
j
的取值范围是0
、1
、2
,表示完成的交易笔数。
3. 状态转移方程:
-
持有股票(
f[i][j]
):-
可能是前一天就持有股票,即
f[i-1][j]
。 -
也可能是今天买入股票,那么前一天必须没有股票,即
g[i-1][j] - prices[i]
。 -
取两者的最大值:
f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i]);
-
-
不持有股票(
g[i][j]
):-
可能是前一天就没有股票,即
g[i-1][j]
。 -
也可能是今天卖出股票,那么前一天必须持有股票,且交易笔数增加 1,即
f[i-1][j-1] + prices[i]
。 -
取两者的最大值:
g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i]);
-
4. 初始化:
-
第 0 天(初始状态):
-
f[0][0] = -prices[0]
:第 0 天买入股票,利润为-prices[0]
。 -
g[0][0] = 0
:第 0 天不持有股票,利润为0
。 -
其他状态初始化为
-INF
(表示不可达状态)。
-
5. 填表:
-
从
i = 1
开始,逐步计算f[i][j]
和g[i][j]
,直到i = n-1
。
6. 返回结果:
-
最终的结果是
max(g[n-1][0], g[n-1][1], g[n-1][2])
,即最后一天不持有股票的最大利润。
优化空间复杂度:
当前的空间复杂度是 O(n)
,可以通过滚动数组的方式优化到 O(1)
:
class Solution {
public:const int INF = 0x3f3f3f3f; // 表示无穷大int maxProfit(vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// 使用滚动数组优化空间复杂度vector<int> f(3, -INF); // 持有股票vector<int> g(3, -INF); // 不持有股票// 初始化f[0] = -prices[0]; // 第 0 天买入股票g[0] = 0; // 第 0 天不持有股票// 填表for (int i = 1; i < n; i++) {vector<int> new_f(3, -INF);vector<int> new_g(3, -INF);for (int j = 0; j < 3; j++) {new_f[j] = max(f[j], g[j] - prices[i]); // 持有股票new_g[j] = g[j]; // 不持有股票if (j >= 1) // 如果该状态存在new_g[j] = max(new_g[j], f[j - 1] + prices[i]); // 卖出股票}f = new_f;g = new_g;}// 找到最后一行的最大值int ret = 0;for (int j = 0; j < 3; j++)ret = max(ret, g[j]);return ret;}
};
总结:
-
使用动态规划的思想,通过状态转移方程计算每天的最大利润。
-
通过滚动数组优化,空间复杂度从
O(n)
降低到O(1)
。 -
最终结果是最后一天不持有股票的最大利润(最多完成两笔交易)。
八、 188. 买卖股票的最佳时机 IV - 力扣(LeetCode)
算法代码:
class Solution {
public:const int INF = 0x3f3f3f3f; // 表示无穷大int maxProfit(int k, vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// 处理一个细节问题:最多只能完成 n/2 笔交易k = min(k, n / 2);// f[i][j] 表示第 i 天结束时,完成了 j 笔交易并持有股票的最大利润// g[i][j] 表示第 i 天结束时,完成了 j 笔交易并不持有股票的最大利润vector<vector<int>> f(n, vector<int>(k + 1, -INF));vector<vector<int>> g(n, vector<int>(k + 1, -INF));// 初始化f[0][0] = -prices[0]; // 第 0 天买入股票g[0][0] = 0; // 第 0 天不持有股票// 填表for (int i = 1; i < n; i++) {for (int j = 0; j <= k; j++) {f[i][j] = max(f[i - 1][j], g[i - 1][j] - prices[i]); // 持有股票g[i][j] = g[i - 1][j]; // 不持有股票if (j >= 1) // 如果该状态存在g[i][j] = max(g[i][j], f[i - 1][j - 1] + prices[i]); // 卖出股票}}// 找到最后一行的最大值int ret = 0;for (int j = 0; j <= k; j++)ret = max(ret, g[n - 1][j]);return ret;}
};
这个代码实现了一个动态规划问题,通常被称为“股票买卖问题(最多完成 k
笔交易)”。问题的核心是:给定一个股票价格数组 prices
和一个整数 k
,你最多可以完成 k
笔交易(买入和卖出算一笔交易),目标是最大化利润。
代码思路解析:
1. 问题分析:
-
每天有两种状态:
-
持有股票(
f[i][j]
):表示第i
天结束时,完成了j
笔交易并持有股票的最大利润。 -
不持有股票(
g[i][j]
):表示第i
天结束时,完成了j
笔交易并不持有股票的最大利润。
-
-
目标是找到最后一天的最大利润。
2. 动态规划定义:
-
定义两个状态数组
f
和g
:-
f[i][j]
表示第i
天结束时,完成了j
笔交易并持有股票的最大利润。 -
g[i][j]
表示第i
天结束时,完成了j
笔交易并不持有股票的最大利润。
-
-
j
的取值范围是0
到k
,表示完成的交易笔数。
3. 状态转移方程:
-
持有股票(
f[i][j]
):-
可能是前一天就持有股票,即
f[i-1][j]
。 -
也可能是今天买入股票,那么前一天必须没有股票,即
g[i-1][j] - prices[i]
。 -
取两者的最大值:
f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i]);
-
-
不持有股票(
g[i][j]
):-
可能是前一天就没有股票,即
g[i-1][j]
。 -
也可能是今天卖出股票,那么前一天必须持有股票,且交易笔数增加 1,即
f[i-1][j-1] + prices[i]
。 -
取两者的最大值:
g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i]);
-
4. 初始化:
-
第 0 天(初始状态):
-
f[0][0] = -prices[0]
:第 0 天买入股票,利润为-prices[0]
。 -
g[0][0] = 0
:第 0 天不持有股票,利润为0
。 -
其他状态初始化为
-INF
(表示不可达状态)。
-
5. 填表:
-
从
i = 1
开始,逐步计算f[i][j]
和g[i][j]
,直到i = n-1
。
6. 返回结果:
-
最终的结果是
max(g[n-1][0], g[n-1][1], ..., g[n-1][k])
,即最后一天不持有股票的最大利润。
优化空间复杂度:
当前的空间复杂度是 O(n * k)
,可以通过滚动数组的方式优化到 O(k)
:
class Solution {
public:const int INF = 0x3f3f3f3f; // 表示无穷大int maxProfit(int k, vector<int>& prices) {int n = prices.size();if (n == 0) return 0; // 边界条件处理// 处理一个细节问题:最多只能完成 n/2 笔交易k = min(k, n / 2);// 使用滚动数组优化空间复杂度vector<int> f(k + 1, -INF); // 持有股票vector<int> g(k + 1, -INF); // 不持有股票// 初始化f[0] = -prices[0]; // 第 0 天买入股票g[0] = 0; // 第 0 天不持有股票// 填表for (int i = 1; i < n; i++) {vector<int> new_f(k + 1, -INF);vector<int> new_g(k + 1, -INF);for (int j = 0; j <= k; j++) {new_f[j] = max(f[j], g[j] - prices[i]); // 持有股票new_g[j] = g[j]; // 不持有股票if (j >= 1) // 如果该状态存在new_g[j] = max(new_g[j], f[j - 1] + prices[i]); // 卖出股票}f = new_f;g = new_g;}// 找到最后一行的最大值int ret = 0;for (int j = 0; j <= k; j++)ret = max(ret, g[j]);return ret;}
};
总结:
-
使用动态规划的思想,通过状态转移方程计算每天的最大利润。
-
通过滚动数组优化,空间复杂度从
O(n * k)
降低到O(k)
。 -
最终结果是最后一天不持有股票的最大利润(最多完成
k
笔交易)。
相关文章:
简单多状态 dp 问题(典型算法思想)—— OJ例题算法解析思路
目录 一、面试题 17.16. 按摩师 - 力扣(LeetCode) 算法代码: 代码思路解析: 问题分析: 动态规划定义: 状态转移方程: 初始化: 填表: 返回值: 优化空…...
【电路笔记 TMS320C6***DSP】外部存储器接口 A EMIFA向FPGA(作为异步存储器)写入数据的示例
目录 DSP和FPGA的连接DSP端:传输数据给FPGAFPGA端:接收数据 EMIFA(External Memory Interface A)的“异步存储器”(Asynchronous Memory)指的是那些不与系统时钟同步进行读写操作的外部存储设备。这类存储器…...
pgsql 查看数据库、表、索引大小等
查询数据库大小 -- 查询单个数据库大小 select pg_size_pretty(pg_database_size(postgres)) as size;-- 查询所有数据库大小 select datname, pg_size_pretty (pg_database_size(datname)) AS size from pg_database; 查询表大小 -- 查询单个表大小 select pg_size_pretty(p…...
物联网感知层采集的数据 经过etl 后 ,输送给ai 训练模型 和模型本身调优
在物联网(IoT)系统中,感知层采集的数据经过 ETL(Extract, Transform, Load) 处理后,可以作为 AI 模型的训练数据,用于模型训练和调优。以下是实现这一过程的详细步骤和技术方案: 一、数据流程概述 数据采集:通过传感器和物联网设备采集原始数据。ETL 处理:对原始数据…...
C语言基础
一、基础 C语言文件 后缀 .c为源文件 .h为头文件 以 Visual studio 为例右键点击源文件点击添加,新建项 .c为C语言文件,.cpp为C文件 后缀不同编译器会按照不同的编译语法进行编译 .cpp以C语法 第一个程序 #include <stdio.h> //包含 st…...
pinginfoview网络诊断工具中文版
介绍 pinginfoview中文版本是一款实用的网络诊断工具,它专为中文用户设计,提供了方便易用的界面,使得在Windows环境下进行ping测试变得更加简单。该工具是由NirSoft开发的一款免费的桌面应用程序,尽管官方可能并未正式发布中文版…...
Anyting LLM LLM温度设置范围
在Anything LLM中,LLM(Language Model)的温度设置是一个关键参数,它影响着模型生成文本时的随机性和确定性。关于Anything LLM的LLM温度设置范围,虽然没有官方的明确数值范围说明,但通常温度参数的设置遵循…...
鸿蒙Android4个脚有脚线
效果 min:number122max:number150Row(){Stack(){// 底Text().border({width:2,color:$r(app.color.yellow)}).height(this.max).aspectRatio(1)// 长Text().backgroundColor($r(app.color.white)).height(this.max).width(this.min)// 宽Text().backgroundColor($r(app.color.w…...
RecyclerView与ListView的优化
RecyclerView与ListView的优化 一、基础概念对比 1.1 ListView与RecyclerView概述 ListView和RecyclerView都是Android中用于展示列表数据的重要控件,但RecyclerView是更现代化的解决方案,提供了更多的灵活性和性能优势。 ListView特点 Android早期…...
【人工智能】GPT-4 vs DeepSeek-R1:谁主导了2025年的AI技术竞争?
前言 2025年,人工智能技术将迎来更加激烈的竞争。随着OpenAI的GPT-4和中国初创公司DeepSeek的DeepSeek-R1在全球范围内崭露头角,AI技术的竞争格局开始发生变化。这篇文章将详细对比这两款AI模型,从技术背景、应用领域、性能、成本效益等多个方…...
2025年Cursor最新安装使用教程
Cursor安装教程 一、Cursor下载二、Cursor安装三、Cursor编辑器快捷键(1) 基础编辑快捷键(2) 导航快捷键(3) 其他常用快捷键 一、Cursor下载 Cursor官方网站(https://www.cursor.com/ ) 根据自己电脑操作系统选择对应安装包 二、Cursor安装 下载完成后…...
原码、反码和补码的介绍和区别
在计算机中,有符号整数的表示方法主要有 原码、反码和补码,它们解决了二进制数表示正负数及简化运算的问题。以下是分步说明: 1. 原码(Sign-Magnitude) 定义:最高位为符号位(0正1负)…...
STM32 进阶 定时器
在stm32中定时器大概分为4类 1、系统定时器:属于arm内核,内嵌在NVIC中 2、高级定时器:可以用来刹车和死区 3、通用定时器:可以用来输出pwm方波 4、基本定时器:只能记数 系统定时器注意: 1、系统定时器…...
山东大学:《DeepSeek应用与部署》
大家好,我是吾鳴。 今天吾鳴要给大家分享一份由山东大学出版的DeepSeek报告——《DeepSeek应用与部署》,这份报告讲述了AIGC的发展历程,DeepSeek应用场景和DeepSeek如何本地化部署。报告一共80页PPT,文末有完整版下载地址。 内容摘…...
【无标题】FrmImport
文章目录 前言一、问题描述二、解决方案三、软件开发(源码)四、项目展示五、资源链接 前言 我能抽象出整个世界,但是我不能抽象你。 想让你成为私有常量,这样外部函数就无法访问你。 又想让你成为全局常量,这样在我的…...
Android14 OTA升级
因Vendor Freeze的缘故,若开启Non-AB OTA, 则会遇到交叉编译vendor和system的增量升级包时需要检查fingerprint而导致编译失败,从而无法做到增量升级包升级。高版本一般都是打开AB模式。 AB 和 non AB 切换相关宏 /vendor_ap_s0/device/mediatek/system/mssi_64_cn/SystemCo…...
监听 RabbitMQ 延时交换机的消息数、OpenFeign 路径参数传入斜杠无法正确转义
背景 【MQ】一套为海量消息和高并发热点消息,提供高可用精准延时服务的解决方案 我现在有一个需求,就是监听 RabbitMQ 一个延时交换机的消息数,而 RabbitTemplate 是不存在对应的方法来获取的。 而我们在 RabbitMQ 的控制台却可以发现延时交…...
宇树科技嵌入式面试题及参考答案(春晚机器人的公司)
目录 设计一个带看门狗(Watchdog)的嵌入式系统,描述故障恢复流程 在资源受限的 MCU 上实现 OTA 升级功能,描述关键设计点 如何实现 OTA(空中升级)功能?描述固件校验和回滚机制的设计要点 推挽输出与开漏输出的区别?举例说明其在 GPIO 控制中的应用 UART、SPI、I2C …...
Linux内核自定义协议族开发指南:理解net_device_ops、proto_ops与net_proto_family
在Linux内核中开发自定义协议族需要深入理解网络协议栈的分层模型。net_device_ops、proto_ops和net_proto_family是三个关键结构体,分别作用于不同的层次。本文将详细解析它们的作用、交互关系及实现方法,并提供一个完整的开发框架。 一、核心结构体的作用与层级关系 struct…...
【Go语言快速上手】第一部分:数据类型(数组、切片、映射)与控制语句
文章目录 一、复合类型Ⅰ 数组1. 语法2. 示例3. 特点4. 数组的传递 Ⅱ 切片1. 定义2. 语法3. 示例4. 特点5. 切片的创建6. 切片的操作切片的扩展切片的拷贝 Ⅲ 映射1. 定义2. 语法3. 示例4. 特点5. 映射的创建6. 映射的操作示例:插入、访问和删除判断键是否存在示例…...
系统架构评估中的重要概念
(1)敏感点(Sensitivity Point) 和权衡点 (Tradeoff Point)。敏感点和权衡点是关键的架构 决策。敏感点是一个或多个构件(和/或构件之间的关系)的特性。研究敏感点可使设计人员 或分析员明确在搞清楚如何实现质量目标时应注意什么。权衡点是影响多个质量属性的特性, …...
shell逐行读取文件 远程操作服务器
代码示例 while read ip; doecho "uninstalling test programs in $line" ssh root$ip bash -s < remote_remove_tool.shdone < installed_ips总结 ✅ 作用: 逐行读取 installed_ips 文件中的 IP 地址通过 SSH 连接到远程服务器ÿ…...
盛铂科技SCP4000射频微波功率计与SPP5000系列脉冲峰值 USB功率计 区别
在射频(RF)和微波测试领域,快速、精准的功率测量是确保通信系统、雷达、卫星设备等高性能运行的核心需求。无论是连续波(CW)信号的稳定性测试,还是脉冲信号的瞬态功率分析,工程师都需要轻量化、…...
【每日八股】计算机网络篇(三):IP
目录 DNS 查询服务器的基本流程DNS 采用 TCP 还是 UDP,为什么?默认使用 UDP 的原因需要使用 TCP 的场景?总结 DNS 劫持是什么?解决办法?浏览器输入一个 URL 到显示器显示的过程?URL 解析TCP 连接HTTP 请求页…...
vtk 3D坐标标尺应用 3D 刻度尺
2d刻度尺 : vtk 2D 刻度尺 2D 比例尺-CSDN博客 简介: 3D 刻度尺,也是常用功能,功能强大 3D 刻度尺 CubeAxesActor vtkCubeAxes调整坐标轴的刻度、原点和显示效果,包括关闭小标尺、固定坐标轴原点,以及设置FlyMode模…...
探秘基带算法:从原理到5G时代的通信变革【十】基带算法应用与对比
文章目录 三、算法在现代通信系统中的应用3.1 5G 通信中的应用3.1.1 信道编码与调制解调3.1.2 大规模 MIMO 技术3.1.3 案例分析:5G 基站与终端实现 3.2 卫星通信中的应用3.2.1 抗干扰与纠错编码3.2.2 信号处理与调制解调3.2.3 案例分析:卫星通信系统实例…...
消费级、工业级、汽车级、军工级、航天级芯片区别对比
汽车电子行业,经常会由于降本原因,听到“消规、工规、车规”方面的讨论。常见的芯片等级一般是按照使用温度、辐射、抗干扰等来分级。等级分为以下5类: 民用级(消费级)、工业级、汽车级(车规级)、军工级、航…...
迷你世界脚本背包接口:Backpack
背包接口:Backpack 彼得兔 更新时间: 2023-10-25 10:29:21 具体函数名及描述如下: 序号 函数名 函数描述 1 getBackpackBarIDRange(...) 获取道具背包栏ID范围(起始ID~结束ID) 2 getBackpackBarSize(...) 获取道具背包栏大小 3 setGridItem(.…...
javaSE基础
java跨平台性: Java 语言编写的程序,一次编译后,可以在多个操作系统上运行。是由于其增加了一个中间件 JVM,JVM 负责将 Java 字节码转换为特定平台的机器码,并执行。 Java 程序从源代码到运行需要经过三步:…...
电脑的系统版本是windows7的,下载pycharm的哪个版本比较好呢?
李升伟 整理 在Windows 7系统上选择PyCharm版本时,需综合考虑系统兼容性、Python版本适配性以及开发需求。以下是具体建议和操作指南: 1. 选择PyCharm的兼容版本 推荐版本:PyCharm 2020.3.5及之前的旧版本。 原因:自2021年起&a…...
【前端基础】3、HTML的常用元素(h、p、img、a、iframe、div、span)、不常用元素(strong、i、code、br)
HTML结构 一个HTML包含以下部分: 文档类型声明html元素 head元素body元素 例(CSDN): 一、文档类型声明 HTML最一方的文档称为:文档类型声明,用于声明文档类型。即:<!DOCTYPE html>…...
安装mysql
1、安装数据库 下载链接 https://downloads.mysql.com/archives/community/ 下载zip安装包,解压到某个路径下,将bin文件夹添加到系统环境变量 。 然后终端输入指令 mysql --version 验证 2、初始化数据库 打开命令提示符(以管理员身份&am…...
关于 QPalette设置按钮背景未显示出来 的解决方法
若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/146047054 长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV…...
记一次ScopeSentry搭建
介绍 Scope Sentry是一款具有资产测绘、子域名枚举、信息泄露检测、漏洞扫描、目录扫描、子域名接管、爬虫、页面监控功能的工具,通过构建多个节点,自由选择节点运行扫描任务。当出现新漏洞时可以快速排查关注资产是否存在相关组件。 目前功能 插件系…...
【每日学点HarmonyOS Next知识】Web Header更新、状态变量嵌套问题、自定义弹窗、stack圆角、Flex换行问题
【每日学点HarmonyOS Next知识】Web Header更新、状态变量嵌套问题、自定义弹窗、stack圆角、Flex换行问题 1、HarmonyOS 有关webview Header无法更新的问题? 业务A页面 打开 webivew B页面,第一次打开带了header请求,然后退出webview B页面…...
优选算法的智慧之光:滑动窗口专题(二)
专栏:算法的魔法世界 个人主页:手握风云 目录 一、例题讲解 1.1. 最大连续1的个数 III 1.2. 找到字符串中所有字母异位词 1.3. 串联所有单词的子串 1.4. 最小覆盖子串 一、例题讲解 1.1. 最大连续1的个数 III 题目要求是二进制数组&am…...
Android ChatOn-v1.66.536-598-[构建于ChatGPT和GPT-4o之上]
ChatOn 链接:https://pan.xunlei.com/s/VOKYnq-i3C83CK-HJ1gfLf4gA1?pwdwzwc# 添加了最大无限积分 删除了所有调试信息 语言:全语言支持...
十一、Redis Sentinel(哨兵)—— 高可用架构与配置指南
Redis Sentinel(哨兵)—— 高可用架构与配置指南 在分布式应用中,Redis 主从复制(Master-Slave)虽然能提供读写分离的能力,但它 无法自动故障转移(failover)。如果主节点(Master)发生故障,系统管理员需要手动将某个从节点(Slave)提升为主节点,并重新配置所有从节…...
【STM32】玩转IIC之驱动MPU6050及姿态解算
目录 前言 一.MPU6050模块介绍 1.1MPU6050简介 1.2 MPU6050的引脚定义 1.3MPU6050寄存器解析 二.MPU6050驱动开发 2.1 配置寄存器 2.2对MPU6050寄存器进行读写 2.2.1 写入寄存器 2.2.2读取寄存器 2.3 初始化MPU6050 2.3.1 设置工作模式 2.3.2 配置采样率 2.3.3 启…...
《张一鸣,创业心路与算法思维》
张一鸣,多年如一日的阅读习惯。 爱读人物传记,称教科书式人类知识最浓缩的书,也爱看心理学,创业以及商业管理类的书。 冯仑,王石,联想,杰克韦尔奇,思科。 《乔布斯传》《埃隆马斯…...
深入浅出:UniApp 从入门到精通全指南
https://juejin.cn/post/7440119937644101684 uni-app官网 uniapp安卓离线打包流程_uniapp离线打包-CSDN博客 本文是关于 UniApp 从入门到精通的全指南,涵盖基础入门(环境搭建、创建项目、项目结构、编写运行)、核心概念与进阶知识&#x…...
低空经济-飞行数据平台 搭建可行方案
搭建一个飞行数据平台是低空经济中至关重要的一环,它能够实现对飞行器的实时监控、数据分析、路径优化以及安全管理。以下是搭建飞行数据平台的详细步骤和技术方案: 一、平台的核心功能 实时监控: 实时获取飞行器的位置、速度、高度、电池状态等数据。提供可视化界面,展示飞…...
【四.RAG技术与应用】【12.阿里云百炼应用(下):RAG的云端优化与扩展】
在上一篇文章中,我们聊了如何通过阿里云百炼平台快速搭建一个RAG(检索增强生成)应用,实现文档智能问答、知识库管理等基础能力。今天咱们继续深入,聚焦两个核心问题:如何通过云端技术优化RAG的效果,以及如何扩展RAG的应用边界。文章会穿插实战案例,手把手带你踩坑避雷。…...
3-7 WPS JS宏 工作表移动复制实例-2(多工作簿的多工作表合并)学习笔记
************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…...
【智能机器人开发全流程:硬件选型、软件架构与ROS实战,打造高效机器人系统】
文章目录 1. 硬件层设计(1) 传感器选型(2) 计算平台 2. 软件架构设计(1) 核心模块划分(2) 通信框架 3. 关键实现步骤(1) 硬件-软件接口开发(2) SLAM与导航实现(3) 仿真与测试 4. 典型框架示例基于ROS的移动机器人分层架构 5. 优化与扩展6. 开源项目参考 1. 硬件层设计 (1) 传感…...
【CSS—前端快速入门】CSS 选择器
CSS 1. CSS介绍 1.1 什么是CSS? CSS(Cascading Style Sheet),层叠样式表,用于控制页面的样式; CSS 能够对网页中元素位置的排版进行像素级精确控制,实现美化页面的效果;能够做到页面的样式和 结构分离; 1…...
二、QT和驱动模块实现智能家居-----5、通过QT控制LED
在QT界面,我们要实现点击“LED”按钮就可以控制板子上的LED。LED接线图如下: 在Linux 系统里,我们可以使用2种方法去操作上面的LED: ① 使用GPIO SYSFS系统:这需要一定的硬件知识,需要设置引脚的方向、数值…...
在UI设计中使用自定义控件
生成: 自定义控件完成: 看控件的父类是谁: 在想使用的窗口中: 拖动一个与他父类相同类型的控件: 点击控件右键 这样就是你自定义的控件了 运行后: //点击QSpinBox,滑块跟着移动void (QSpinBox::…...
docker 离线安装redis(离线)
docker离线安装redis时,我找了挺多资料都是需要先在另一台能联网的机器上先下载镜像,然后移动内网中的docker服务器进行部署,我也是这么操作的,但是必须拥有能上网的docker环境才能下载,我在github上找到了一种可以直接…...
利用python实现对Excel文件中数据元组的自定义排序
问题引入: 假设你是一个浙江省水果超市的老板,统筹11个下辖地市的水果产量。假设11个地市生产的水果包括:苹果、香蕉和西瓜。你如何快速得到某种水果产量突出(排名前几)的地市?产量落后(排名后…...