当前位置: 首页 > article >正文

系统学习算法:动态规划(斐波那契+路径问题)

题目一:

思路:

作为动态规划的第一道题,这个题很有代表性且很简单,适合入门

先理解题意,很简单,就是斐波那契数列的加强版,从前两个数变为前三个数

算法原理:

这五步可以说是所有动态规划的通用步骤,基本按照这个逻辑步骤,正确地完成每一个步骤,最后就能得到正确结果

1.状态表示:

基本所有的动态规划都要先创建一个dp表,也就是一维数组或者二维数组,而这个数组中的每一个空对应的含义就是状态表示

像本道题,我们要创建一个一维数组,而数组下标n对应的值就应该为第n个泰波那契数,这个状态表示就是我们所定义的,这个是最重要的一步,跟之前递归专题的定义递归函数功能是一样的,你想怎么定义就怎么定义,这道题很明显这么定义就很符合第一反应,因此这就完成了动态规划的第一步,状态表示

2.状态转移方程

用人话来说就是靠这个方程将dp表所有给填满,而这一步就是动态规划最难的一步,这道题题目已经给出了状态转移方程,那就是dp[n]=dp[n-1]+dp[n-2]+dp[n-3],所以我们就是根据这个状态转移方程去填这个dp表,当然其他动态规划题就不会像本题一样简单,就需要自己去推导状态转移方程

3.初始化

其实就跟数独一样,题目得先给你一些存在的数,你才能去填其他的空,不能什么都不给,就让你填,而初始化就是先填上一部分dp表的空,然后状态转移方程通过初始化的值去填剩下的空

本题就是给了前三个泰波那契数0,1,1,这样我们才能通过状态转移方程往后推出更多的泰波那契数

同时也这一步也用于避免填表时越界的问题,比如当n<=2时,根据状态转移方程,就会出现访问-1,-2下标的值,就越界了,所以我们需要对这些进行特判

4.填表顺序

其实就是从左往右填,还是从右往左填,这道题很明显是根据前面三个泰波那契数推导当前的泰波那契数,根据状态表示,也就是数组靠前的是前面的泰波那契数,因此就是从左往右填

5.返回值

dp表填完后,那就该返回答案了,根据状态表示,确定返回值,比如本题状态表示是下标为n的代表第n个泰波那契数,那么题目要求我们返回第n个泰波那契数,那么我们就返回下标为n的就可以了,这时题目也就解决了

代码1:

class Solution {public int tribonacci(int n) {//初始化加特判if(n<=2){if(n==0){return 0;}else{return 1;}}int[] dp=new int[n+1];dp[0]=0;dp[1]=1;dp[2]=1;//状态转移方程并填表for(int i=3;i<=n;i++){dp[i]=dp[i-1]+dp[i-2]+dp[i-3];}//返回值return dp[n];}
}

而这道题和后面的背包问题可以使用空间优化的方法,也就是滚动数组

我们可以发现有些数用完其实就没用了,就像一次性筷子一样,那么用完就该扔了,同理我们的数组其实就没必要继续保留那些用过的值了

像我们这道题,就只用4个变量,就可以解决了,abc代表前三个泰波那契数,d表示当前的泰波那契数,然后到下一个泰波纳锲数时,通过滚动的操作,将上一回的bcd赋给abc,然后再求出d,如此类推

这样我们的时间复杂度就从原来的O(N)变为O(1),所以这种办法可以降下一个指数级别的复杂度

代码2(空间优化):

class Solution {public int tribonacci(int n) {//初始化加特判if(n<=2){if(n==0){return 0;}else{return 1;}}int a=0,b=1,c=1,d=0;for(int i=3;i<=n;i++){//状态转移方程d=a+b+c;//滚动数组a=b;b=c;c=d;}//返回值return d;}
}

题目二:

思路:

题意很简单,就是每次可以有3种跳台阶的选择,然后返回到某一阶台阶有多少种跳法 

以n=3为例,小孩从第0阶为出发点,可以选择(1,2,3)每次跳1步到达第3阶,也可以选择(1,3)第一次跳1步,第二次跳2步到达第3阶,也可以选择(2,3)第一次跳2步,第二次跳1步到达第3阶,还可以选择直接跳3步到第3阶

总共4种跳法,所以返回4

算法原理:

还是按照动态规划的五个步骤走

1.状态表示:

dp表的第i个下标表示到达第i阶有多少种跳法

2.状态转移方程:

因为有3种跳法,所以如果要到第i阶,只能从i-3阶,i-2阶,i-1阶分别跳3步,2步,1步才能到达第i阶,而dp[i-3],dp[i-2],dp[i-1]记录着到达对应台阶的跳法,因此状态转移方程为dp[i]=dp[i-1]+dp[i-2]+dp[i-3]

3.初始化:

因为有三种跳法,所以至少要初始化前3个台阶,易知dp[1]=1,dp[2]=2,dp[3]=4,所以初始化这三个值然后并进行特判

4.填表顺序:

从低台阶跳到高台阶,而dp表前面的值记录的低台阶的跳法,后面的就记录更高台阶的跳法,所以填表顺序是从左往右填

5.返回值:

因为状态表示中dp[i]表示第i阶有多少种跳法,所以第n阶有多少种跳法要返回dp[n],问题就解决了

代码:

class Solution {public int waysToStep(int n) {//初始化并且特判if(n==1||n==2){return n;}if(n==3){return 4;}//求模数,e代表科学计数法,且默认为double类型int mod=(int)1e9+7;int[] dp=new int[n+1];dp[1]=1;dp[2]=2;dp[3]=4;//状态转移方程for(int i=4;i<=n;i++){dp[i]=((dp[i-1]+dp[i-2])%mod+dp[i-3])%mod;}//返回值return dp[n];}
}

其实这道题跟上面那道题几乎一模一样,因此也可以用滚动数组进行空间优化,就不多说了

题目三:

思路:

题意也很简单,就是可以选择跳1步还是跳2步,然后每次跳完要给钱,找到最小花费的值,唯一需要注意的是终点并不是最后一个元素,而是最后一个元素的后面,也就是数组越界的位置

算法原理:

 还是五个步骤

1.状态表示

一般都是以dp表的第i个位置,表示题目的要求,本题那就是dp[i]就表示,到达第i阶时所需要的最少花费

2.状态转移方程

一般根据dp表之前的填空情况来推导当前填表的答案,dp[i]表示第i阶时所需要的最少花费,那么如果要到第i阶,那么就只能从第i-1阶或者i-2阶开始跳,如果要求第i阶时所需要的最少花费,那么就从第i-1阶或者i-2阶的最少花费再加上对应的花费选较少的那个,而第i-1阶或者i-2阶的最少花费又是由dp[i-1]和dp[i-2]记录,因此就形成了闭环

所以状态转移方程:dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

3.初始化

题目说可以从0下标和1下标开始跳,那么所以dp[0]=dp[1]=0,避免了当i过小时数组越界

4.填表顺序

还是从下往上跳,那么dp表左边的指向低台阶,dp表右边指向高台阶,高台阶的最少花费由低台阶的最少花费决定,所以填表顺序是从左往右

5.返回值

因为最后要跳出数组,所以要返回dp[n],因此dp表在创建的时候要多创一个位置new int[n+1]

问题就解决了

代码1:

class Solution {public int minCostClimbingStairs(int[] cost) {//初始化int n=cost.length;int[] dp=new int[n+1];dp[0]=dp[1]=0;//状态转移方程for(int i=2;i<=n;i++){dp[i]=Math.min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);}//返回值return dp[n];}
}

同理也可以逆向思考

刚刚的状态表示是dp[i]为到第i阶的最少花费,那么也可以反过来,dp[i]为从第i阶为起点,到顶的最少花费,那么初始化也是先初始dp[n-1],dp[n-2],填表顺序也变为从右往左,返回值则是dp[0]和dp[1]的最小值

代码2:

class Solution {public int minCostClimbingStairs(int[] cost) {//初始化int n=cost.length;int[] dp=new int[n];dp[n-1]=cost[n-1];dp[n-2]=cost[n-2];//状态转移方程for(int i=n-3;i>=0;i--){dp[i]=Math.min(dp[i+1],dp[i+2])+cost[i];}//返回值return Math.min(dp[0],dp[1]);}
}

题目四:

 思路:

题意不是很难理解,就是返回一段字符串有多少种解码方法

需要注意的就是6是合法解码方式,06不是合法解码方式,最大解码为26,如果有一处出现了无法解码,那么整条解码方法就为0

算法原理:

还是五个步骤:

1.状态表示 

一般都是以某一个位置为起点或结尾+题目要求,本题就以i位置为结尾,有多少种解码数

2.状态转移方程

根据题意我们可以知道,要么是单独解码,要么是连续两个一起解码

这时就开始分类讨论

如果是单独解码,如果不为0,那么此时解码数就加上前一个位置的解码数,如果为0,那就不能单独解码,就不加上前一个位置的解码数,但也不能直接就认为解码失败,因为还有连续解码的情况,比如10,20

然后是连续解码,如果与前一个位置连起来在10-26之间,那么就说明能被连续解码,这时前一个位置和当前位置就成为了一个整体,因此加上的是i-2位置的解码数,反之,不在10-26之间就不加i-2位置的解码数

因为我们状态表示是以i位置为结尾,所以我们只用分析与前一个位置的连续解码情况,而不用管后面的位置

所以总体来说状态转移方程为

dp[i]+=dp[i-1](单独解码如果成功)+dp[i-2](连续解码如果成功)

3.初始化

为了避免越界情况,所以至少要先初始化dp[0]和dp[1]

dp[0]前面没有元素,那么只能单独解码,所以如果是0就为0,不是0就为1

dp[1]前面有dp[0],所以可以单独解码也可以连续解码,同理单独解码时如果是0就不加,不是0就加1,连续解码时如果为10-26就加1,不是就不加

4.填表顺序

因为是以i位置为结尾,所以是根据前面填好的情况来填后面的空,因此是从左往右

5.返回值

因为要返回整个解码总数,所以是以n-1位置为结尾有多少解码方法,而状态表示以i位置为结尾,有多少种解码数,所以就返回dp[n-1]

代码1:

class Solution {public int numDecodings(String s) {//初始化int n = s.length();int[] dp = new int[n];char[] ch = s.toCharArray();//特判dp[0]和dp[1]if (ch[0] != '0') {dp[0] = 1;}if (n == 1) {return dp[0];}//单独解码if (ch[0] != '0' && ch[1] != '0') {dp[1] += 1;}//连续解码int num = (ch[0] - '0') * 10 + (ch[1] - '0');if (num >= 10 && num <= 26) {dp[1] += 1;}//状态转移方程for (int i = 2; i < n; i++) {//单独解码if (ch[i] != '0') {dp[i] += dp[i - 1];}//连续解码int num = (ch[i - 1] - '0') * 10 + (ch[i] - '0');if (num >= 10 && num <= 26) {dp[i] += dp[i - 2];}}//返回值return dp[n - 1];}
}

可以发现在初始化特判的时候非常冗杂,甚至比状态转移方程写的还多,而其中dp[1]的初始化流程和状态转移方程几乎一致,因此能不能直接将dp[1]的初始化流程放到状态转移方程中完成,当然是可以的

这里就用到了有些动态规划题目可以采用虚拟节点的方法进行初始化

就是多创建一个空,然后所有往后移动一位

dp[1]就是原来的dp[0],那么还是只有单独解码的可能,因此是0就为0,不为0就为1,然后dp[2]就是原来的dp[1],根据状态转移方程 

dp[i] = dp[i-1](单独解码如果成功)+dp[i-2](连续解码如果成功)

所以此时dp[2-2]应该设置为1,因为如果能连续解码,那么就加1,因此虚拟节点就初始化为1,如果连续解码不成功,那么就根本用不到dp[2-2],无所谓dp[2-2]的值,所以综上要将dp[0]初始化为1

代码2:

class Solution {public int numDecodings(String s) {//初始化int n = s.length();int[] dp = new int[n+1];char[] ch = s.toCharArray();//dp[0]和dp[1]dp[0]=1;if (ch[0] != '0') {dp[1] = 1;}//状态转移方程for (int i = 2; i <= n; i++) {//单独解码if (ch[i-1] != '0') {dp[i] += dp[i - 1];}//连续解码int num = (ch[i - 2] - '0') * 10 + (ch[i-1] - '0');if (num >= 10 && num <= 26) {dp[i] += dp[i - 2];}}//返回值return dp[n];}
}

题目五:

思路:

之前在记忆化搜索的章节做过这道题,因为记忆化搜索和动态规划其实大差不差,因此也可以用动态规划做

算法原理:

这道题因为涉及了二维表,与之前一维表有些不一样,但步骤都是那五步

1.状态表示

 因为是二维表,那么就是以dp[ i ][ j ]为结尾,加上题目要求,到达ij位置时的路径和

2.状态转移方程

因为只能向右或向下,所以到达该位置时,只能从上面到下面,从左边到右边,因此状态转移方程

dp[ i ] [ j ]=dp[i-1][ j-1]+dp[ i ][ j-1]

3.初始化

因为处于最左边和最上边的边界格子,在状态转移方程中会越界,所以要对此进行特殊处理,而在一维中是采用多开一个位置,即虚拟节点,而在二维中,往往会在左边多开一列,上边多开一行,那么边界情况就全部解决了,不会越界,而在起点dp[1][1]应该为1,所以可以直接给dp[1][1]赋值为1,也可以在dp[0][1]或者dp[1][0]赋值为1,然后dp[1][1]再通过状态转移方程赋值为1,都是可以的

4.填表顺序

因为是根据上边和左边来决定当前位置,因此应该是从上往下,且从左往右填表的

5.返回值

dp[ i ][ j ]表示为当前位置的路径和,题目要求返回到达终点有多少种不同路径,那么我们就返回dp[m][n]即可

代码:

class Solution {public int uniquePaths(int m, int n) {//初始化int[][] dp=new int[m+1][n+1];dp[0][1]=1;//状态转移方程for(int i=1;i<=m;i++){for(int j=1;j<=n;j++){dp[i][j]=dp[i-1][j]+dp[i][j-1];}}//返回值return dp[m][n];}
}

题目六:

思路:

跟上一道题几乎一模一样,就是多了障碍物这个设置

算法原理: 

还是五个步骤,除了状态转移方程与上一题不太一样,其他都一样

因为出现了障碍物,所以这个格子的dp值一定为0,因此只需要在填表时判断一下是否有障碍物,如果有就直接设为0,如果不是障碍物,则继续套用上一题的状态转移方程

代码:

class Solution {public int uniquePathsWithObstacles(int[][] obstacleGrid) {//初始化int row=obstacleGrid.length;int col=obstacleGrid[0].length;int[][] dp=new int[row+1][col+1];dp[0][1]=1;//状态转移方程for(int i=1;i<=row;i++){for(int j=1;j<=col;j++){if(obstacleGrid[i-1][j-1]==1){dp[i][j]=0;}else{dp[i][j]=dp[i-1][j]+dp[i][j-1]; }}}//返回值return dp[row][col];}
}

题目七:

思路:

还是路径问题,依然可以采用动态规划,题意很简单,就是从左上角为起点,右下角为终点,移动方向只能向下或向右,然后返回路径上的最大值

算法原理:

 依旧是五个步骤

1.状态表示

以i j位置为终点,到达这里的路径最大值

2.状态转移方程

因为只有向下和向右两个方向,那么能到达[i][ j ]位置的只能是[i-1][ j ],或者[i][ j-1] ,而又要找到当前位置路径最大值,那么就选择这两格的最大值,再加上当前位置的值就是当前位置的最大值,因此状态转移方程是

dp[i][ j ]=Math.max(dp[i-1][ j ],[i][ j-1]) + frame[i][ j ]

3.初始化

依旧多加一行多加一列,方便填表时不越界

4.填表顺序

根据移动方向可知,填表顺序是从上往下,从左往右

5.返回值

根据状态表示,返回dp[m][n]

代码:

class Solution {public int jewelleryValue(int[][] frame) {//初始化int row=frame.length;int col=frame[0].length;int[][] dp=new int[row+1][col+1];//状态转移方程for(int i=1;i<=row;i++){for(int j=1;j<=col;j++){dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+frame[i-1][j-1];}}//返回值return dp[row][col];}
}

题目八:

思路:

依旧是路径问题,只是这回移动方向变成了只能向下,且左右可以对角移动,最后返回最小下降路径的值

算法原理:

依旧是五个步骤

1.状态表示

以i j位置为终点,表示到达此位置时的最小下降路径和

2.状态转移方程

因为方向多了对角线,且只能从上一行下来,因此为[i-1][ j-1 ],[i-1][ j ],[i-1][ j+1 ]

而要找的是最小的,所以就是状态转移方程为

dp[i][ j ]=Math.min(dp[i-1][ j-1 ],Math.min(dp[i-1][ j ],dp[i-1][ j+1 ]))

3.初始化

为了避免越界问题,所以依旧要扩容来将原数组包围,所以就是多加一行,但是这回就要多加两列,因为左边要有一列,右边要有一列,这样才能全部包围住,不会越界

因为要找最小的,所以扩容的位置的值不能初始化为0,不然会影响到真正的结果,因此要初始化为最大值

4.填表顺序

根据移动方向顺序,还是从上到下,从左往右

5.返回值

最后都会到达最后一行,所以只用返回最后一行的最小dp值即可

代码:

class Solution {public int minFallingPathSum(int[][] matrix) {//初始化int row=matrix.length;int col=matrix[0].length;int[][] dp=new int[row+1][col+2];for(int i=1;i<=row;i++){dp[i][0]=dp[i][col+1]=Integer.MAX_VALUE;}//状态转移方程for(int i=1;i<=row;i++){for(int j=1;j<=col;j++){dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i-1][j+1]))+matrix[i-1][j-1];}}//返回值int ret=dp[row][1];for(int j=2;j<=col;j++){ret=ret>dp[row][j]?dp[row][j]:ret;}return ret;}
}

题目九:

思路:

还是路径问题,只是变成要求最小值,其他的要求和之前珠宝那道题目几乎一样

算法原理:

依旧是五步:

1.状态表示

以i j位置为终点,表示到达此位置时的最小路径和

2.状态转移方程

因为只有向下和向右两个方向,那么能到达[i][ j ]位置的只能是[i-1][ j ],或者[i][ j-1] ,而又要找到当前位置路径最小值,那么就选择这两格的最小值,再加上当前位置的值就是当前位置的最小值,因此状态转移方程是

dp[i][ j ]=Math.min(dp[i-1][ j ],[i][ j-1]) + grid[i][ j ]

3.初始化

依旧多加一行多加一列,方便填表时不越界,只是这里要求最小值,所以扩容的位置要初始化为MAX,而从起点开始,因此它的上一格或者左一格任选一个初始化为0即可

4.填表顺序

根据移动方向可知,填表顺序是从上往下,从左往右

5.返回值

根据状态表示,返回dp[m][n]

代码:

class Solution {public int minPathSum(int[][] grid) {//初始化int row=grid.length;int col=grid[0].length;int[][] dp=new int[row+1][col+1];for(int j=2;j<=col;j++){dp[0][j]=Integer.MAX_VALUE;}for(int i=1;i<=row;i++){dp[i][0]=Integer.MAX_VALUE;}//状态转移方程for(int i=1;i<=row;i++){for(int j=1;j<=col;j++){dp[i][j]=Math.min(dp[i-1][j],dp[i][j-1])+grid[i-1][j-1];}}//返回值return dp[row][col];}
}

题目十:

 思路:

依旧是路径,题意也很好理解,跟游戏中打怪boss一样,正数表示为补给包,负数代表有怪物,0表示为空房间,每次只能选择进入右边的房间或者下边的房间,从左上角出发,到右下角结束,但过程中要保持有血量(路径和)要大于等于1,问一开始起点初始的最低血量

以示例1为例子,按照该路径走可以算出起始最低血量为7,但不要认为就是-2+-3+3+1+-5=-6,所以直接-6取反再加1就可以了,这里就容易出现一个误区,那就是这个-6是所有路径上所需血量的最小值,这道题刚好是最后一格是所有路径上的所需血量的最小值,所以是-6

如果还有疑惑,举个游戏中的例子就明白,你进入一个房间,遇到超级大怪兽,假设打倒需要10000血,然后打完这个怪兽后面有个房间有9999血的补给包,那这里你能直接

10000-9999+x(初始血量)>=1,x=2,能这样算嘛,你都打不过那个怪兽,后面补给包不可能拿到的

初始最低血量应该为10001滴血

算法原理:

依旧是路径问题,大致步骤也是一样的

但是从第一步就容易出错,根据之前的题目,我们状态表示都是以某个位置为结尾,怎么这么样,而这道题用以结尾这种状态表示则是错误的,因为具有“有后效性”

可以简单去尝试下,就是填dp表时,刚根据上左dp值得出当前dp值,但是后面依然会影响当前dp值

如上图,假设设置状态表示为到达ij位置时,所需最低初始健康点数

那么-2这个格根据上左扩容的dp就应该为3,到了-3这个格子,发现最低应该是6,3这个格子为6,然后到3这个格子,你就不知道怎么填了,因为你不能6-3就填3吧,那就犯了一开始的误区,也不能不管仍然填6吧,那后面就完全乱套了,最后返回-5这个格子的dp值肯定是错的,这就是被后面的状态给影响了,也就是“有后效性”

简单来说还是被后面的血包影响了,以为你能欠血后面再还,实际是全程都不能低于1

因此就需要改变状态表示,反正不是以结尾,就是以起点

1.状态表示

以i j为起点,所需最低初始健康点数

2.状态转移方程

因为是以起点,那么就需要根据后面的dp值来填,即右格和下格的dp值

因为要找最低的,所以是用右格和下格的min,然后再减去当前格子dungeon的值,也就是dp值

当然因为dungeon有可能为正数(血包),所以减去后有可能为负数,此时又犯了欠血的误区,因此要if判断一下,如果为负数,那么就直接修改为1,因为至少需要活着

所以状态转移方程为

dp[i][j]=Math.min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];

                if(dp[i][j]<=0){

                    dp[i][j]=1;

                }

3.初始化

为了避免越界问题,所以要将原数组包围起来,因此要多加一行多加一列,但只用包越界的那边,因为这次是向右和向下,所以围的是右边和下边

因为要找最小值,所以应该初始化为MAX,因为终点下右的都是初始化的,所以要任选一个更改为1,表示最低需要1滴血

4.填表顺序

如果状态表示是以起点,那么填表顺序就反过来了,从下往上,从右往左

5.返回值

根据状态表示返回第一格的dp就是

代码:

class Solution {public int calculateMinimumHP(int[][] dungeon) {//初始化int row=dungeon.length;int col=dungeon[0].length;int[][] dp=new int[row+1][col+1];for(int i=0;i<=row;i++){dp[i][col]=Integer.MAX_VALUE;}for(int j=0;j<=col;j++){dp[row][j]=Integer.MAX_VALUE;}dp[row-1][col]=1;//状态转移方程for(int i=row-1;i>=0;i--){for(int j=col-1;j>=0;j--){dp[i][j]=Math.min(dp[i+1][j],dp[i][j+1])-dungeon[i][j];if(dp[i][j]<=0){dp[i][j]=1;}}}//返回值return dp[0][0];}
}

相关文章:

系统学习算法:动态规划(斐波那契+路径问题)

题目一&#xff1a; 思路&#xff1a; 作为动态规划的第一道题&#xff0c;这个题很有代表性且很简单&#xff0c;适合入门 先理解题意&#xff0c;很简单&#xff0c;就是斐波那契数列的加强版&#xff0c;从前两个数变为前三个数 算法原理&#xff1a; 这五步可以说是所有…...

jquery实现文字点选验证码

文字点选验证码是一种有效的防止自动化攻击的手段。用户需要按照提示顺序点击特定的文字&#xff0c;验证通过后才能进行下一步操作。本文将详细介绍如何使用jQuery实现这种验证码。 一、实现思路 生成验证码&#xff1a;随机生成一组文字&#xff0c;并随机排列在验证码区域…...

VTK|加载ply文件数据进行平移+高程渲染

文章目录 将 .ply 点云或模型数据进行 Elevation 着色并可视化渲染的完整流程&#x1f7e6; 1. **使用 ElevationFilter 给模型上色&#xff08;根据 Z 值&#xff09;**&#x1f7e9; 2. **构造 Jet 风格的 Lookup Table&#xff08;颜色映射表&#xff09;**&#x1f537; 3.…...

JAVA房屋租售管理系统房屋出租出售平台房屋销售房屋租赁房屋交易信息管理源码

一、源码描述 这是一套房屋租售管理源码&#xff0c;基于SpringBootVue框架&#xff0c;后端采用JAVA开发&#xff0c;源码功能完善&#xff0c;涵盖了房屋租赁、房屋销售、房屋交易等业务。 二、源码截图...

掌握Multi-Agent实践(三):ReAct Agent集成Bing和Google搜索功能,采用推理与执行交替策略,增强处理复杂任务能力

一个普遍的现象是,大模型通常会根据给定的提示直接生成回复。对于一些简单的任务,大模型或许能够较好地应对。然而,当我们面对更加复杂的任务时,往往希望大模型能够表现得更加“智能”,具备适应多样场景和解决复杂问题的能力。为此,AgentScope 提供了内置的 ReAct 智能体…...

AOP实现原理

AOP实现原理 背景实现常用注解 背景 感觉需要掌握, 对理解其他知识点有好处. 实现 动态代理实现. JDK 实现 InvacationHander CGLib Enhancer 轻量级的基于ASM字节码框架. 常用注解 Before After AfterRetruning AfterThrowing Around...

Bearer Token的神秘面纱:深入解析HTTP认证头的设计哲学

为何有些Token会带Bearer&#xff1f; 在接口测试与开发中&#xff0c;我们经常会遇到这样的请求头&#xff1a; Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... 这个神秘的"Bearer"前缀从何而来&#xff1f;为何不直接使用Authorization: Token..…...

【国产化】在银河麒麟ARM环境下离线安装docker

1、前言 采用离线安装的方式。 关于离线安装的方式官网有介绍&#xff0c;但是说的很简单&#xff0c;网址&#xff1a;Binaries | Docker Docs 官网介绍的有几种主流linux系统的安装方式&#xff0c;但是没有kylin的&#xff0c;所以在此记录一下。 在安装过程中也遇到了些…...

K8S - Harbor 镜像仓库部署与 GitLab CI 集成实战

引言 在 Kubernetes 环境中&#xff0c;容器镜像的存储与管理至关重要。企业级镜像仓库&#xff08;如 Harbor&#xff09;为团队提供了安全、稳定、可扩展的镜像管理解决方案。 一、Harbor 安装与配置 Harbor 是由 VMware 开源的企业级云原生镜像仓库&#xff0c;它不仅支持…...

ASCII码的快速记忆方法

当然&#xff01;记住ASCII码的关键是找到规律和分组记忆。以下是一些快速记忆的方法&#xff1a; 1. 记住关键分界点 0~31&#xff1a;控制字符&#xff08;不可打印&#xff0c;如换行、制表符等&#xff09;&#xff0c;不需要全记&#xff0c;知道0是NULL&#xff0c;10是…...

java volatile关键字

volatile 是 Java 中用于保证多线程环境下变量可见性和禁止指令重排序的关键字。 普通变量不加volatile修饰有可见性问题&#xff0c;即有线程修改该变量值&#xff0c;其他线程无法立即感知该变量值修改了。代码&#xff1a; private static int intVal 0; // 普通变量未加 …...

解决SQL Server SQL语句性能问题(9)——正确使用索引

前述章节中,我们介绍和讲解了SQL调优所需要的基本知识和分析方法,那么,通过前述这些知识和方法定位到问题后,接下来,我们该怎么做呢?那就是本章的内容,给出解决SQL语句性能问题的、科学而合理的方案和方法。 本章主要对解决SQL语句性能问题的几种常用方法进行说明和讲解…...

Vibe Coding: 优点与缺点

如果你最近在开发圈子里,你很可能听说过这个新趋势"vibe coding"(氛围编程)。 我只能说我对此感受复杂。以下是原因。 优势 在构建新项目时,靠着氛围编程达到成功感觉很自由!但对于遗留代码来说情况就不同了,尽管也不是不可能。 实时反馈和快速迭代 Cursor(…...

技术分享 | 如何在2k0300(LoongArch架构)处理器上跑通qt开发流程

近期迅为售后团队反馈&#xff0c;许多用户咨询&#xff1a;2K0300处理器采用了LA264处理器核&#xff0c;若要在该处理器上运行Qt程序&#xff0c;由于架构发生了变化&#xff0c;其使用方法是否仍与ARM平台保持一致&#xff1f; 单纯回答‘一致’或‘不一致’缺乏说服力&…...

产品经理如何借助 DeepSeek 提升工作效能

在数字化时代的浪潮中&#xff0c;产品经理肩负着推动产品从概念到成功落地的重任&#xff0c;面临着复杂多变的市场环境、层出不穷的用户需求以及紧锣密鼓的项目周期。而 DeepSeek 这一先进的人工智能工具&#xff0c;宛如一把 “瑞士军刀”&#xff0c;为产品经理在各个工作环…...

基于卷积神经网络和Pyqt5的猫狗识别小程序

任务描述 猫狗分类任务&#xff08;Dogs vs Cats&#xff09;是Kaggle平台在2013年举办的一个经典计算机视觉竞赛。官方给出的Kaggle Dogs vs Cats 数据集中包括由12500张猫咪图片和12500张狗狗图片组成的训练集&#xff0c;12500张未标记照片组成的测试集。选手需要在规定时间…...

Hadoop 和 Spark 生态系统中的核心组件

以下是 Hadoop 和 Spark 生态系统的核心组件及其功能&#xff1a; Hadoop 生态核心组件 1. HDFS&#xff08;Hadoop 分布式文件系统&#xff09; - 命令/工具&#xff1a; hdfs 命令&#xff08;如 hdfs dfs -put 等&#xff09;。 - 作用&#xff1a;分布式存储海量数据&a…...

解锁健康养生新境界

在追求高品质生活的当下&#xff0c;健康养生早已超越 “治未病” 的传统认知&#xff0c;成为贯穿全生命周期的生活艺术。它如同精密的交响乐&#xff0c;需饮食、运动、心理与生活习惯多维度协奏&#xff0c;方能奏响生命的强音。 饮食养生讲究 “顺时、适性”。遵循二十四节…...

MQTT:轻量级物联网通信协议详解

引言 在物联网&#xff08;IoT&#xff09;迅速发展的今天&#xff0c;设备之间的高效通信变得至关重要。MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;作为一种轻量级的发布/订阅消息传输协议&#xff0c;因其低带宽、低延迟和易于实现的特性&#xff0c…...

C 语言逻辑运算符:组合判断,构建更复杂的条件

各类资料学习下载合集 ​​https://pan.quark.cn/s/8c91ccb5a474​​ 在 C 语言编程中,我们已经学习了如何使用比较运算符(如 ​​==​​​, ​​<​​​, ​​>​​)来判断两个值之间的关系,从而得到“真”或“假”的结果。但很多时候,我们需要根据多个条件的组合…...

基于OpenCV的人脸识别:EigenFaces算法

文章目录 引言一、概述二、代码解析1. 准备工作2. 加载训练图像3. 设置标签4. 准备测试图像5. 创建和训练识别器6. 进行预测7. 显示结果 三、代码要点总结 引言 人脸识别是计算机视觉领域的一个重要应用&#xff0c;今天我将通过一个实际案例来展示如何使用OpenCV中的EigenFac…...

【深度学习新浪潮】智能追焦技术全解析:从算法到设备应用

一、智能追焦技术概述 智能追焦是基于人工智能和自动化技术的对焦功能,通过深度学习算法识别并持续跟踪移动物体(如人、动物、运动器械等),实时调整焦距以保持主体清晰,显著提升动态场景拍摄成功率。其核心优势包括: 精准性:AI 算法优化复杂运动轨迹追踪(如不规则移动…...

网络研讨会开发注册中, 5月15日特励达力科,“了解以太网”

在线研讨会主题 Understanding Ethernet - from basics to testing & optimization 了解以太网 - 从基础知识到测试和优化 注册链接# https://register.gotowebinar.com/register/2823468241337063262 时间 北京时间 2025 年 5 月 15 日 星期四 下午 3:30 - 4:30 适宜…...

在 Spring Boot 中实现动态线程池的全面指南

动态线程池是一种线程池管理方案&#xff0c;允许在运行时根据业务需求动态调整线程池参数&#xff08;如核心线程数、最大线程数、队列容量等&#xff09;&#xff0c;以优化资源利用率和系统性能。在 Spring Boot 中&#xff0c;动态线程池可以通过 Java 的 ThreadPoolExecut…...

STL?vector!!!

一、前言 之前我们借助手撕string加深了类和对象相关知识&#xff0c;今天我们将一起手撕一个vector&#xff0c;继续深化类和对象、动态内存管理、模板的相关知识 二、vector相关的前置知识 1、什么是vector&#xff1f; vector是一个STL库中提供的类模板&#xff0c;它是存储…...

从黔西游船侧翻事件看极端天气预警的科技防线——疾风气象大模型如何实现精准防御?

近日,贵州省黔西市一起载人游船侧翻事故令人痛心。调查显示,事发时当地突遇强风暴雨,水面突发巨浪导致船只失控。这一事件再次凸显:在极端天气频发的时代,传统“经验式防灾”已不足够,唯有依靠智能化的气象预警技术,才能筑牢安全底线。 极端天气预警的痛点:为什么传统方…...

LDO与DCDC总结

目录 1. 工作原理 2. 性能对比 3. 选型关键因素 4. 典型应用 总结 1. 工作原理 LDO LDO通过线性调节方式实现降压&#xff0c;输入电压需略高于输出电压&#xff08;压差通常为0.2-2V&#xff09;&#xff0c;利用内部PMOS管或PNP三极管调整压差以稳定输出电压。其结构简单…...

FastChat部署大模型

一、前提条件 1、系统环境&#xff08;使用的 autodl 算力平台&#xff09; 2、安装相关库 安装 modescope pip3 install -U modelscope # 或使用下方命令 # pip3 install -U modelscope -i https://mirror.sjtu.edu.cn/pypi/web/simple安装 fastchat git clone https://gi…...

智汇云舟亮相第二十七届北京科博会

5月8日&#xff0c;备受瞩目的第二十七届中国北京国际科技产业博览会&#xff08;以下简称&#xff1a;北京科博会&#xff09;在国家会议中心盛大开幕。作为我国科技领域的重要盛会&#xff0c;北京科博会汇聚了众多前沿科技成果与创新力量&#xff0c;为全球科技产业交流搭建…...

Redis最新入门教程

文章目录 Redis最新入门教程1.安装Redis2.连接Redis3.Redis环境变量配置4.入门Redis4.1 Redis的数据结构4.2 Redis的Key4.3 Redis-String4.4 Redis-Hash4.5 Redis-List4.6 Redis-Set4.7 Redis-Zset 5.在Java中使用Redis6.缓存雪崩、击穿、穿透6.1 缓存雪崩6.2 缓冲击穿6.3 缓冲…...