算法33:返回组成aim的最少货币张数 ----- 从左往右尝试模型

之前对从左到右尝试模型的累加和进行过总结了,概括为:

1. 针对固定集合,值不同,就是讨论要和不要的累加和。算法30有完整的例子

2. 针对非固定集合,面值固定,张数无限。口诀就是讨论要与不要,要的话逐步讨论要几张的累加和。算法31有完整的例子

3. 针对非固定集合,面值固定,张数随机。也就是说有可能只有0张,1张,2张,甚至也是无限的情况。口诀就是在口诀2的基础之上要去除多余项。算法32有完整的例子
 
那么接下来还是从左往右的尝试模型练习,但是这一次并不是要求累加和的问题了,而是要求最小值。

题目:

arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。 每个值都认为是一种面值,且认为张数是无限的。 返回组成aim的最少货币张数。

分析:

1. arr是面值数组,面值固定且没有重复,张数无限。那不就是直接套用口诀2嘛。口诀2是累加和,那我们换成最少张数不就行了。

2. 讨论要与不要,要的话要几张。然后对组合成aim值的张数求最小值。

推导:

1. 假设数组为 {1,2}, aim值为4.

2. 不要1,要2.  要1张aim值只能为2。要2张aim值为4.

3. 不要1,不要2. aim只能为0. 否则没有办法组合成aim为4。

4. 要1,不要2.  要1张,aim只能为1;要2张,aim只能为2,依次类推。只有4张1才能组成4.

5. 要1,要2.   要1张1,1张2. aim只能为3;2张1,1张2,aim可以为4.

步骤2为2张;步骤4为4张;步骤5为3张。最少张数肯定取步骤2的张组合成aim为4.即取2张

递归代码:

 public static int way(int arr[], int aim){//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int ans = process(arr, 0, aim);return Integer.MAX_VALUE == ans ? -1 : ans;}public static int process (int[] arr, int index, int aim){//数组越界if (index == arr.length) {//如果目标值为0, 则需要0张货币; 否则,无法凑到aim值,返回无效值return aim == 0 ? 0 : Integer.MAX_VALUE;}int count = Integer.MAX_VALUE;for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {//返回张数int ans = process(arr, index + 1, aim - zhangshu * arr[index]);//如果返回值有效if (ans != Integer.MAX_VALUE) {count = Math.min(count, zhangshu + ans);}}return count;}

动态规划:

动态规划是可以根据递归直接得到的。技巧就是递归的base case就是动态规划初始行的值。递归的逻辑直接照搬到动态规划的代码中来。

而本例子中递归的base case为 :

if (index == arr.length) {//如果目标值为0, 则需要0张货币; 否则,无法凑到aim值,返回无效值return aim == 0 ? 0 : Integer.MAX_VALUE;
}

1.首先构建二维表

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)
row = 1
(value = 2)
row = 2
 

2. 根据递归的base case :

if (index == arr.length) {//如果目标值为0, 则需要0张货币; 否则,无法凑到aim值,返回无效值return aim == 0 ? 0 : Integer.MAX_VALUE;
} 

可得到:

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)
row = 1
(value = 2)
row = 2
 
0maxmaxmaxmax

3. 根据递归的逻辑:

int count = Integer.MAX_VALUE;
for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {//返回张数int ans = process(arr, index + 1, aim - zhangshu * arr[index]);//如果返回值有效if (ans != Integer.MAX_VALUE) {count = Math.min(count, zhangshu + ans);}
}

推导row下标为1的行:

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)
row = 1
(value = 2)

aim为0,

value为2,

需要0张

aim可为0.

此处取0

aim为1,

value为2,

需要0张

无法推导

需要1张

无法推导

取max无效值

aim为2,

value为2,

需要0张

无法推导

需要1张

可得aim为2

此处取 1 张

aim为3,

value为2,

需要0张

无法推导

需要1张

无法推导

需要2张

无法推导

取Max无效值

aim为4,

value为2,

需要0张

无法推导

需要1张

无法推导

需要2张

可得aim为4

此处取 2 张

row = 2
 
0maxmaxmaxmax

4. 推导row下标为0的行

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)

aim为0

value为1.

需要0张

可得aim为0

此处取0

aim为1

value为1.

需要0张

无法推导

需要1张

可得aim为1

此处取1

aim为2

value为1.

需要0张

无法推导

需要0张1和1张2

可以推导

此处取1

需要1张

无法推导

需要2张

可得aim为2。

此处取2

1 < 2. 此处最终取1

aim为3

value为1.

需要0张

无法推导

需要1张

无法推导

需要1张1和1张2

可以推导

此处取2

需要2张

无法推导

需要3张

可得aim为3

此处取3

2 < 3, 此处最终取2

aim为4

value为1.

需要0张

无法推导

需要0张1和2张2

可以推导

此处取2

需要1张

无法推导

需要2张

无法推导

需要2张1和1张2

可以推导

此处取3

需要3张

无法推导

需要4张

可得aim为4

此处取4

2<3<4, 最终取2

row = 1
(value = 2)

aim为0,

value为2,

需要0张

aim可为0.

此处取0

aim为1,

value为2,

需要0张

无法推导

需要1张

无法推导

取max无效值

aim为2,

value为2,

需要0张

无法推导

需要1张

可得aim为2

此处取 1 张

aim为3,

value为2,

需要0张

无法推导

需要1张

无法推导

需要2张

无法推导

取Max无效值

aim为4,

value为2,

需要0张

无法推导

需要1张

无法推导

需要2张

可得aim为4

此处取 2 张

row = 2
 
0maxmaxmaxmax

那么,最终的推导结果为:

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)

0

1

1

2

2
row = 1
(value = 2)

0

max

1

max

2

row = 2
 
0maxmaxmaxmax

因此,动态规划代码可以这么改:

 //动态规划public static int dp1(int[] arr, int aim) {//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] dp = new int[N + 1][aim + 1];//根据递归base case : index == arr.length可得,dp最后一行的初始值dp[N][0] = 0;for (int i = 1; i <= aim; i++) {//无效值dp[N][i] = Integer.MAX_VALUE;}//动态规划老套路,双层for循环。以arr数组为行,以aim为列//最后一行已经初始化,需要从倒数第二行开始推导for (int row = N - 1; row >= 0; row--) {for (int col = 0; col <= aim; col++) {//从左往右尝试模型,开始讨论要与不要。要的话,到底要几张//完全照抄递归的逻辑int count = Integer.MAX_VALUE;for (int zhangshu = 0; zhangshu * arr[row] <= col; zhangshu++) {//返回张数。递归中此处是递归的调用。此处直接换成依赖关系int ans = dp[row + 1][col - (zhangshu * arr[row])];//如果返回值有效if (Integer.MAX_VALUE != ans) {count = Math.min(count, zhangshu + ans);}}dp[row][col] = count;}}return dp[0][aim] == Integer.MAX_VALUE ? -1 : dp[0][aim];}

我们以肉眼可见的速度发现,动态规划的时间复杂度为 O(arr.length * aim * aim).  而之前我们已经分析过此类动态规划是可以对时间复杂度进行优化的。下面进行优化过程的逐步推导:

aim = 0aim = 1aim =2aim = 3aim = 4
row = 0
(value = 1)

0

1

1

2

2
row = 1
(value = 2)

0

max

1

max

2

row = 2
 
0maxmaxmaxmax

假设aim为6 ,结果该如何呢?

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

1

1

2

2
row = 1
(value = 2)

0

max

1

max

2

row = 2
 
0maxmaxmaxmax

value为1的情况,肯定是6张

value为2的情况,肯定是3张

value为1和2的组合,最少张数是多少呢?数数手指头肯定是2张2和2张1的组合呀,即4张。

推导:

第一步

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

1

1

2

2
row = 1
(value = 2)

0

max

1

max

2

row = 2
 
0maxmaxmaxmaxmaxmax

第二步:

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

1

1

2

2
row = 1
(value = 2)

0

max

1

max

2

5 - 1 * 2 = 3.即依赖dp[1][3]处的值。但是dp[1][3] = max代表无效值

dp[2][5] = max 代表无效值。

此处取max

6-1*2 = 4,即依赖dp[1][4]处的值。

dp[1][4] = 2. aim为

4.  

那此处aim == 6不就是

4 + 1张*value = 4 + 1*2 = 6。

也就是说:

dp[1][6] = dp[1][6-value] + 1

即:dp[1][6] = dp[1][4] + 1 = 2 + 1 = 3, 即取3张

dp[2][6] = max无效值

因此,此处为3。

row = 2
 
0maxmaxmaxmaxmaxmax

第三步:

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

1

1

2

2

5 - 1*value = 5-1*1= 4

即依赖dp[]0[4]的张数值

dp[]0[4]的aim为4. 

5 = 4 + 1张 * value 

   = 4+1*1 = 5.

即需要dp[0][4] + 1 = 2 + 1=3. 即取3张。

dp[1][5]无法组成aim为5的情况,无效值max

因此,此处取3

6-1*value= 6-1*1=5

即依赖dp[0][5]处的值。

dp[0][5] 的aim为5

6 = 5 + 1张 * value

= 5 + 1 * 1 = 6。

即需要dp[0][5] + 1 = 3 + 1 = 4张。

dp[1][6] = 3.

3 < 4. 取3.

此处的最小货币数为3

row = 1
(value = 2)

0

max

1

max

2

5 - 1 * 2 = 3.即依赖dp[1][3]处的值。但是dp[1][3] = max代表无效值。

dp[2][5] = max,代表无效值。

此处取max

6-1*2 = 4,即依赖dp[1][4]处的值。

dp[1][4] = 2. aim为

4.  

那此处aim == 6不就是

4 + 1张*value = 4 + 1*2 = 6。

也就是说:

dp[1][6] = dp[1][6-value] + 1

即:dp[1][6] = dp[1][4] + 1 = 2 + 1 = 3

即取3张

dp[2][6] = max无效值

因此,此处为3。

row = 2
 
0maxmaxmaxmaxmaxmax

既然有这样的归类,那么我们之前的0、1、2、3、4是不是也具有这样的规则呢?尝试一下看看

首先尝试row=1的行

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

1

1

2

2

5 - 1*value = 5-1*1= 4

即依赖dp[]0[4]的张数值

dp[]0[4]的aim为4. 

5 = 4 + 1张 * value 

   = 4+1*1 = 5.

即需要dp[0][4] + 1 = 2 + 1=3. 即取3张。

dp[1][5]无法组成aim为5的情况,无效值max

因此,此处取3

6-1*value= 6-1*1=5

即依赖dp[0][5]处的值。

dp[0][5] 的aim为5

6 = 5 + 1张 * value

= 5 + 1 * 1 = 6。

即需要dp[0][5] + 1 = 3 + 1 = 4张。

dp[1][6] = 3.

3 < 4. 取3.

此处的最小货币数为3

row = 1
(value = 2)

0

max

结论:1

尝试:

aim-1张*value = 2-1*2=0.

即依赖dp[1][0]处的值。

dp[1][0] + 1 = 0 + 1 = 1.即取1

dp[2][2] =max,无效值

最终取1

结果:

成立

max

结论:2

尝试:

aim-1张*value = 4-1*2=2.

即依赖dp[1][2]处的值。

dp[1][2] + 1 = 1 + 1 = 2.即取2

dp[2][4] =max,无效值

最终取2

结果:

成立

5 - 1 * 2 = 3.

即依赖dp[1][3]处的值。

但是dp[1][3] = max

代表无效值。

dp[2][5] = max,

代表无效值。

此处取max

6-1*2 = 4,即依赖dp[1][4]处的值。

dp[1][4] = 2. aim为

4.  

那此处aim == 6不就是

4 + 1张*value = 4 + 1*2 = 6。

也就是说:

dp[1][6] = dp[1][6-value] + 1

即:dp[1][6] = dp[1][4] + 1 = 2 + 1 = 3

即取3张

dp[2][6] = max无效值

因此,此处为3。

row = 2
 
0maxmaxmaxmaxmaxmax

接下来,我们再来尝试row=0的行:

aim = 0aim = 1aim =2aim = 3aim = 4aim=5aim=6
row = 0
(value = 1)

0

结论:1

尝试:

aim-1张*value = 1-1*1=0.

即依赖dp[1][0]处的值。

dp[1][0] + 1 = 0 + 1 = 1.即取1

dp[2][2] =max,无效值

最终取1

结果:

成立

结论:1

尝试:

aim-1张*value = 2-1*1=1.

即依赖dp[0][1]处的值。

dp[0][1] + 1 = 1 + 1 = 1.即取2

dp[2][2] =1,即取1

1< 2, 最终取1

结果:

成立

结论:2

尝试:

aim-1张*value = 3-1*1=3.

即依赖dp[0][2]处的值。

dp[0][2] + 1 = 1 + 1 = 2.即取2

dp[2][3] =max,无效值

最终取2

结果:

成立

结论:2

尝试:

aim-1张*value = 4-1*1=0.

即依赖dp[0][3]处的值。

dp[0][3] + 1 = 2 + 1 = 3.即取3

dp[2][4] = 2, 即取2

2 < 3, 最终取2

结果:

成立

5 - 1*value = 5-1*1= 4

即依赖dp[]0[4]的张数值

dp[]0[4]的aim为4. 

5 = 4 + 1张 * value 

   = 4+1*1 = 5.

即需要dp[0][4] + 1 = 2 + 1=3. 即取3张。

dp[1][5]无法组成aim为5的情况,无效值max

因此,此处取3

6-1*value= 6-1*1=5

即依赖dp[0][5]处的值。

dp[0][5] 的aim为5

6 = 5 + 1张 * value

= 5 + 1 * 1 = 6。

即需要dp[0][5] + 1 = 3 + 1 = 4张。

dp[1][6] = 3.

3 < 4. 取3.

此处的最小货币数为3

row = 1
(value = 2)

0

max

结论:1

尝试:

aim-1张*value = 2-1*2=0.

即依赖dp[1][0]处的值。

dp[1][0] + 1 = 0 + 1 = 1.即取1

dp[2][2] =max,无效值

最终取1

结果:

成立

max

结论:2

尝试:

aim-1张*value = 4-1*2=2.

即依赖dp[1][2]处的值。

dp[1][2] + 1 = 1 + 1 = 2.即取2

dp[2][4] =max,无效值

最终取2

结果:

成立

5 - 1 * 2 = 3.

即依赖dp[1][3]处的值。

但是dp[1][3] = max

代表无效值。

dp[2][5] = max,

代表无效值。

此处取max

6-1*2 = 4,即依赖dp[1][4]处的值。

dp[1][4] = 2. aim为

4.  

那此处aim == 6不就是

4 + 1张*value = 4 + 1*2 = 6。

也就是说:

dp[1][6] = dp[1][6-value] + 1

即:dp[1][6] = dp[1][4] + 1 = 2 + 1 = 3

即取3张

dp[2][6] = max无效值

因此,此处为3。

row = 2
 
0maxmaxmaxmaxmaxmax

我们发现,按照这样的推导过程,结果全部正确。既然正确,说明这个公式是大概率成立的。那么,我们尝试对动态代码的时间复杂度进行优化:

动态规划 + 时间复杂度:

//动态规划 + 时间复杂度优化public static int dp2(int[] arr, int aim) {//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] dp = new int[N + 1][aim + 1];//根据递归index == arr.length可得,dp最后一行的初始值dp[N][0] = 0;for (int i = 1; i <= aim; i++) {//无效值dp[N][i] = Integer.MAX_VALUE;}//动态规划老套路,双层for循环。以arr数组为行,以aim为列//最后一行已经初始化,需要从倒数第二行开始推导for (int row = N - 1; row >= 0; row--) {for (int col = 0; col <= aim; col++) {//下一行当前列dp[row][col] =  dp[row + 1][col];//col - arr[row] : 前一个value对应的下标有效if (col - arr[row] >= 0&& dp[row][col - arr[row]] != Integer.MAX_VALUE) {/*** 此处,为什么 dp[row][col - arr[row]] + 1 ?* * dp[row][col - arr[row]]为当前行前一个aim对应的下标。 那么当前列的aim* 肯定是 dp[row][col - arr[row]]对应的aim 加上 1 * value。* * 即 dp[row][col]对应的aim = dp[row][col - value]对应的aim + 1 * value* 说的简单点,就是当前行多加1个value值而已。* * 既然value值需要多加上1张,那么张数肯定是也是要多加1的。* * 前一个value列 和 下一行当前列,肯定是取小* */dp[row][col] = Math.min(dp[row][col], dp[row][col - arr[row]] + 1);}}}return dp[0][aim] == Integer.MAX_VALUE ? -1 : dp[0][aim];}

这一切都只是我们的推理而已,那么接下来就是海量数据的测试了。加上对数器进行测试:

完整代码

package code03.动态规划_07.lesson4;/*** arr是面值数组,其中的值都是正数且没有重复。再给定一个正数aim。* 每个值都认为是一种面值,且认为张数是无限的。** 返回组成aim的最少货币张数*/
public class MinPaperNoLimit_07 {public static int way(int arr[], int aim){//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int ans = process(arr, 0, aim);return Integer.MAX_VALUE == ans ? -1 : ans;}public static int process (int[] arr, int index, int aim){//数组越界if (index == arr.length) {//如果目标值为0, 则需要0张货币; 否则,无法凑到aim值,返回无效值return aim == 0 ? 0 : Integer.MAX_VALUE;}int count = Integer.MAX_VALUE;for (int zhangshu = 0; zhangshu * arr[index] <= aim; zhangshu++) {//返回张数int ans = process(arr, index + 1, aim - zhangshu * arr[index]);//如果返回值有效if (ans != Integer.MAX_VALUE) {count = Math.min(count, zhangshu + ans);}}return count;}//动态规划public static int dp1(int[] arr, int aim) {//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] dp = new int[N + 1][aim + 1];//根据递归base case : index == arr.length可得,dp最后一行的初始值dp[N][0] = 0;for (int i = 1; i <= aim; i++) {//无效值dp[N][i] = Integer.MAX_VALUE;}//动态规划老套路,双层for循环。以arr数组为行,以aim为列//最后一行已经初始化,需要从倒数第二行开始推导for (int row = N - 1; row >= 0; row--) {for (int col = 0; col <= aim; col++) {//从左往右尝试模型,开始讨论要与不要。要的话,到底要几张//完全照抄递归的逻辑int count = Integer.MAX_VALUE;for (int zhangshu = 0; zhangshu * arr[row] <= col; zhangshu++) {//返回张数。递归中此处是递归的调用。此处直接换成依赖关系int ans = dp[row + 1][col - (zhangshu * arr[row])];//如果返回值有效if (Integer.MAX_VALUE != ans) {count = Math.min(count, zhangshu + ans);}}dp[row][col] = count;}}return dp[0][aim] == Integer.MAX_VALUE ? -1 : dp[0][aim];}//动态规划 + 时间复杂度优化public static int dp2(int[] arr, int aim) {//-1代表无效if (aim < 0 || arr == null || arr.length == 0) {return -1;}int N = arr.length;int[][] dp = new int[N + 1][aim + 1];//根据递归index == arr.length可得,dp最后一行的初始值dp[N][0] = 0;for (int i = 1; i <= aim; i++) {//无效值dp[N][i] = Integer.MAX_VALUE;}//动态规划老套路,双层for循环。以arr数组为行,以aim为列//最后一行已经初始化,需要从倒数第二行开始推导for (int row = N - 1; row >= 0; row--) {for (int col = 0; col <= aim; col++) {//下一行当前列dp[row][col] =  dp[row + 1][col];//col - arr[row] : 前一个value对应的下标有效if (col - arr[row] >= 0&& dp[row][col - arr[row]] != Integer.MAX_VALUE) {/*** 此处,为什么 dp[row][col - arr[row]] + 1 ?** dp[row][col - arr[row]]为当前行前一个aim对应的下标。 那么当前列的aim* 肯定是 dp[row][col - arr[row]]对应的aim 加上 1 * value。** 即 dp[row][col]对应的aim = dp[row][col - value]对应的aim + 1 * value* 说的简单点,就是当前行多加1个value值而已。** 既然value值需要多加上1张,那么张数肯定是也是要多加1的。** 前一个value列 和 下一行当前列,肯定是取小**/dp[row][col] = Math.min(dp[row][col], dp[row][col - arr[row]] + 1);}}}return dp[0][aim] == Integer.MAX_VALUE ? -1 : dp[0][aim];}// 为了测试public static int[] randomArray(int maxLen, int maxValue) {int N = (int) (Math.random() * maxLen);int[] arr = new int[N];boolean[] has = new boolean[maxValue + 1];for (int i = 0; i < N; i++) {do {arr[i] = (int) (Math.random() * maxValue) + 1;} while (has[arr[i]]);has[arr[i]] = true;}return arr;}// 为了测试public static void printArray(int[] arr) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {/*  int[] arr = {1,5, 14, 2};int aim = 16;System.out.println(way(arr, aim));System.out.println(dp1(arr, aim));System.out.println(dp2(arr, aim));*/int maxLen = 10;int maxValue = 20;int testTime = 1000000;System.out.println("测试开始");for (int i = 0; i < testTime; i++) {int[] arr = randomArray(maxLen, maxValue);int aim = (int) (Math.random() * maxValue);int ans1 = way(arr, aim);int ans2 = dp1(arr, aim);int ans3 = dp2(arr, aim);if (ans1 != ans2 || ans1 != ans3) {System.out.println("Oops!");printArray(arr);System.out.println(aim);System.out.println(ans1);System.out.println(ans2);System.out.println(ans3);break;}}System.out.println("测试结束");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/616327.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STM32H5培训(一)总览

文章目录 1. 前言2. STM32H5系列MCU的特点和新功能包括性能提升、新外设和安全功能等3. STM32H5系列型号之间的区别和关键资源对比4. 性能和功能亮点6. 开发生态参考&#xff1a; 1. 前言 本篇主要介绍STM32H5系列MCU的特点和新功能&#xff0c;包括全新的M33内核、250M主频处…

软件测试|selenium 元素无此属性NoSuchAttributeException问题分析与解决

简介 在使用Selenium进行Web自动化测试时&#xff0c;我们可能会遇到NoSuchAttributeException异常。这个异常通常在尝试访问一个元素的属性&#xff08;attribute&#xff09;时抛出&#xff0c;但该属性不存在。本文将介绍NoSuchAttributeException异常的常见原因以及解决方…

Vue-15、Vue条件渲染

1、v-show 在Vue中&#xff0c;v-show是一个指令&#xff0c;用于根据表达式的值来控制元素的显示与隐藏。当指令的值为true时&#xff0c;元素显示&#xff1b;当指令的值为false时&#xff0c;元素隐藏。 v-show的用法如下&#xff1a; <!DOCTYPE html> <html lan…

基于stm32f103c8t6连接阿里云上传信息

很抱歉,作为一个AI助手,我无法提供一篇基于STM32F103C8T6连接阿里云上传信息的完整博客文章。但我可以为您提供一个大概的步骤概述和一些关键点,帮助您构思或撰写这样的博客: 标题:利用STM32F103C8T6单片机实现与阿里云IoT平台的数据传输 --- **引言** 简述STM32微控制…

代码随想录二刷 |二叉树 | 二叉搜索树的最小绝对差

代码随想录二刷 &#xff5c;二叉树 &#xff5c; 二叉搜索树的最小绝对差 题目描述解题思路 & 代码实现递归法迭代法 题目描述 530.二叉搜索树的最小绝对差 给你一棵所有节点为非负值的二叉搜索树&#xff0c;请你计算树中任意两节点的差的绝对值的最小值。 示例&#…

从0开始python学习-47.pytest框架之解决Template替换无法进行二次处理的问题之热加载

问题&#xff1a;当参数为动态参数时&#xff0c;Template替换无法进行二次处理 解决方案&#xff1a;通过热加载来进行替换 热加载原理&#xff1a;在代码运行的过程中动态的调用python中的方法达到获得动态参数 可以进行热加载的情况&#xff1a;yaml文件&#xff0c;随机…

用我这套模板,几分钟做出文档网站!

大家好&#xff0c;我是保姆皮&#xff0c;最近我上线了自己的《编程宝典》网站&#xff0c;可以在线阅读我分享过的各种编程学习路线和知识干货。 指路&#xff1a;https://codefather.cn/ 不少小伙伴催我出教程&#xff0c;说也想做个类似的文档网站。 所以我用最快的速度出了…

程序员如何写高水平简历?(附模板)

Q&#xff1a;什么是高水平的简历&#xff1f; A&#xff1a;满足HR需求的同时&#xff0c;最大化的体现自身价值的简历是高水平的简历 HR的需求是什么&#xff1f; ✅ HR想看到清晰专业的简历模板 ——家人们每天看几百份简历谁懂啊&#xff01;花里胡哨真看不下去一点&…

OpenAI 自带的检索功能好用吗?定量测评带你深度了解!

向量数据库的劲敌来了&#xff1f;又有一批赛道创业公司要倒下&#xff1f; …… 这是 OpenAI 上线 Assistant 检索功能后&#xff0c;技术圈传出的部分声音。原因在于&#xff0c;此功能可以为用户提供基于知识库问答的 RAG&#xff08;检索增强生成&#xff09; 能力。而此前…

深入理解 MyBatis-Plus 批量保存方法

前言 在项目开发中&#xff0c;需要插入批量插入20多万条数据&#xff0c;通过日志观察&#xff0c;发现在调用MyBatis-Plus中的saveBatch()方法性能非常的差&#xff0c;本篇文章主要分享一下saveBatch()的原理以及使用的注意事项 原理 我们通过源码的形式进行解析saveBatc…

生信步骤|Orthofinder寻找直系同源基因

在探究功能基因的时候&#xff0c;我们经常需要对所研究的基因/蛋白质进行聚类分组&#xff0c;逐个类群探究蛋白功能。创建同源群可以采用Otrhofinder软件&#xff0c;该软件安装方便&#xff0c;运行快速&#xff0c;调用简单&#xff0c;可以说是构建同源群的神器。从OrthoF…

逆变器简述

一般家用电器&#xff0c;像微波炉&#xff0c;电饭煲等都是直接插墙壁上的电就可以工作&#xff0c;所以这些家用电器是使用我们市电AC220V的电压 但我们用到的手机&#xff0c;电瓶车以及新能源汽车都是需要充电器的&#xff0c;所以这些用电产品里面都是有电池的&#xff0…

统计学-R语言-4.1

文章目录 前言编写R函数图形的控制和布局par函数layout函数 练习 前言 安装完R软件之后就可以对其进行代码的编写了。 编写R函数 如果对数据分析有些特殊需要&#xff0c;已有的R包或函数不能满足&#xff0c;可以在R中编写自己的函数。函数的定义格式如下所示&#xff1a; …

在微服务架构中认证和授权的那些事儿

在微服务架构中认证和授权是最基础的服务能力&#xff0c;其中这一块行业类的标准就是OAuth2 和 SSO &#xff0c;而OAuth2 和 SSO 可以归类为“用户管理和身份验证”工具&#xff0c;OpenID Connect 1.0是 OAuth 2.0 协议之上的一个简单身份层。 Part.1 认识OAuth 2.0 OAuth…

低代码与小程序开发:简化创新,加速应用开发

随着信息技术的迅速发展&#xff0c;应用程序的需求与日俱增。然而&#xff0c;传统的软件开发往往耗时、复杂&#xff0c;对于许多企业和开发者来说&#xff0c;开发应用程序的成本和难度成为了一道门槛。而近年来&#xff0c;低代码开发和小程序开发的兴起为解决这一难题带来…

RocketMQ源码阅读-Producer发消息

RocketMQ源码阅读-Producer发消息 1. 从单元测试入手2. 启动过程3. 同步消息发送过程4. 异步消息发送过程5. 小结 Producer是消息的生产者。 Producer和Consummer对Rocket来说都是Client&#xff0c;Server是NameServer。 客户端在源码中是一个单独的Model&#xff0c;目录为ro…

Torch not compiled with CUDA enabled

最近接触chatglm3对话预训练模型&#xff0c;从git上下载&#xff0c;装包装半天&#xff0c;最后终于跑起来了&#xff0c;但是一对他进行对话&#xff0c;后台就开始报错了 File "E:\Python311\Lib\site-packages\torch\nn\modules\linear.py", line 114, in forw…

【c++】入门4

内联函数声明和定义不能分开 inline不建议声明和定义分离&#xff0c;分离会导致链接错误。因为inline被展开&#xff0c;就没有函数地址 了&#xff0c;链接就会找不到。 auto关键字 随着程序越来越复杂&#xff0c;程序中用到的类型也越来越复杂&#xff0c;经常体现在&…

JavaWEB学习笔记 2024-1-5 --HTML

JavaWEB 上一篇 个人整理非商业用途&#xff0c;欢迎探讨与指正&#xff01;&#xff01; 文章目录 JavaWEB1.HTML1.1HTML中的三大基本标签1.2显示标签1.2.1文本标签1.2.2表单标签1.2.2.1input标签1.2.2.2下拉框1.2.2.3多行文本1.2.2.4表单标签的通用属性1.2.2.5form标签1.2…

用JAVA实现樱花飘落

用java实现一个樱花飘落的方法 package Text2;import javax.swing.*; import java.awt.*; import java.util.ArrayList; import java.util.List;public class Sakura extends JFrame {private List<Point> sakuraList; // 樱花的位置列表public Sakura() {sakuraList n…