系统学习算法:专题十二 记忆化搜索

news2025/2/25 12:01:09

什么是记忆化搜索,我们先用一道经典例题来引入,斐波那契数

题目一:

相信一开始学编程语言的时候,就一定碰到过这道题,在学循环的时候,我们就用for循环来解决,然后学到了递归,我们又用递归来解决,因此我们大概率第一反应是用递归

代码(递归):

class Solution {
    public int fib(int n) {
        //递归结束条件
        if(n==0||n==1){
            return n;
        }
        //递归公式
        return fib(n-1)+fib(n-2);
    }
}

虽然通过了,但是运行时间却很落后了

因此我们要分析时间复杂度

 以n=5时,画出递归展开图

可以发现每次都是以二叉树展开,那么时间复杂度就是O(2^N),是指数级别

指数级别当然不算很好的时间复杂度,可能看n=5时觉得还好,但是一旦超过一定值,那么就会发生指数爆炸,那么消耗时间增长幅度就很恐怖了

因此我们要分析一下能不能优化,很容易发现,其中有很多重复的操作,一开始左分支就求过d(3)了,右分支又要求一遍d(3),求了2次d(3),同理,d(2)也重复求了3次,那么就知道了为什么消耗时间会很高,因为有大部分的时间都在干重复的事情,而这个事情重复干没有意义,完全可以进行优化

那么我们的想法当然是搞一个“备忘录”,这样子我们干完一件事,把结果放进备忘录里,每次干之前都去备忘录里看看,有没有结果能直接用,没有那就说明之前没干过,这是第一次干,那么只能老老实实去干,如果干过了那么直接就抄结果就好了,就好比数学公式,能记住公式结果直接用就好了,不能每次都从头推导吧,太浪费时间了

那么这个备忘录要怎么实现呢,要根据题目的可变参数和返回类型来创建,不同题目实现的备忘录也不同,像我们这道题,可变参数是第n个斐波那契数,n是整型,而这个数的返回值也是整型,所以这个备忘录的映射关系就是<int,int>,那么就可以用数组或哈希表来作为备忘录

这道题数据范围不大,0—30,用数组就足够了

因此我们就用数组作为备忘录,而备忘录一开始要初始化,初始化的时候要记住,必须是返回值之外的数,像斐波那契数一定大于等于0,那么我们就初始化为-1,这样我们遇见-1就知道这是没干过的,如果初始化为0,那么遇见0就有两种可能,返回值就为0或者没干过的,就不确定了

代码(记忆化搜索):

class Solution {
    int[] memo=new int[31];
    //初始化
    public void begin(int[] memo){
        for(int i=0;i<memo.length;i++){
            memo[i]=-1;
        }
    }
    public int dfs(int n){
        //如果之前干过,直接从备忘录拿结果
        if(memo[n]!=-1){
            return memo[n];
        }
        if(n==0||n==1){
            return n;
        }
        //往备忘录添加
        memo[n]=dfs(n-1)+dfs(n-2);
        //返回
        return memo[n];
    }
    public int fib(int n) {
        begin(memo);
        return dfs(n);
    }
}

时间直接来到0ms,分析一下时间复杂度,因为只需要计算一次第0个到第n个斐波那契数列,剩下直接查找拿结果就是了,查找的时间复杂度是常数级,忽略不计,所以时间复杂度为O(N),来到线性级复杂度,已经很优秀了

而还有一种解法,就是动态规划,而动态规划和记忆化搜索很像

完全只是换了一种方式,但思路都是一样的,即将已经计算过的值存起来

代码(动态规划):

class Solution {
    int[] dp=new int[31];
    public int fib(int n) {
        dp[0]=0;
        dp[1]=1;
        for(int i=2;i<=n;i++){
            dp[i]=dp[i-1]+dp[i-2];
        }
        return dp[n];
    }
}

时间复杂度也是O(N)

但这道题的最优解其实是矩阵快速幂,其时间复杂度为O(logN),但主要用这道题来引入记忆化搜索,就不扩展了

到此,对记忆化搜索有了一定理解了,那就是带记忆的去dfs

题目二:

思路:

题意很简单, 就是机器人只能向右和下移动,问有多少条路可以从左上角到达右下角

先用暴搜的方法来写的话,应该没什么难度

我们假设dfs的功能是返回到这个格子有多少种路径,又因为只能向右和向下,所以到达当前格子的路径等于当前位置的左边格子和当前位置的上边格子的路径之和

所以递归公式就找到了:dfs(i,j)= dfs(i-1,j)+ dfs(i,j-1)

而结束条件就是为起点的时候,路径只有一条,而边界情况,越界的不可能有路径,所以返回0

代码(暴搜):

class Solution {
    public int uniquePaths(int m, int n) {
        //返回到(m-1,n-1)这个点的路径数
        return dfs(m-1,n-1);
    }
    public int dfs(int i,int j){
        //起点
        if(i==0&&j==0){
            return 1;
        }
        //越界
        if(i==-1||j==-1){
            return 0;
        }
        //递归公式
        return dfs(i-1,j)+dfs(i,j-1);
    }
}

结果会超时,分析递归展开图,以(4,4)这个点为例

 可以看到这里就出现了重复的步骤,所以要采用记忆化搜索

还是先搞备忘录,初始化为-1,起点记录1,接下来就是进去先看看是否合法,合法再查找备忘录,有就拿,没有就运算再添加

代码(记忆化搜索):

class Solution {
    int row,col;
    //备忘录
    int[][] memo;
    public int dfs(int i,int j){
        //起点
        if(i==0&&j==0){
            memo[i][j]=1;
            return 1;
        }
        //越界        
        if(i==-1||j==-1){
            return 0;
        }
        //查备忘录
        if(memo[i][j]!=-1){
            return memo[i][j];
        }
        //添加到备忘录
        memo[i][j]=dfs(i-1,j)+dfs(i,j-1);
        //返回结果
        return memo[i][j];
    }
    public int uniquePaths(int m, int n) {
        memo=new int[m][n];
        //初始化
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                memo[i][j]=-1;
            }
        }
        return dfs(m-1,n-1);
    }
}

当然也可以转为动态规划

代码(动态规划):

class Solution {
    public int uniquePaths(int m, int n) {
        //多一行和一列就不用考虑越界了
        int[][] dp = new int[m + 1][n + 1];
        //起点
        dp[1][1] = 1;
        //遍历
        for (int i = 1; i <= m; i++){
            for (int j = 1; j <= n; j++) {
                if (i == 1 && j == 1){
                    continue;
                }
                //越界的默认为0
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }            
        }
        return dp[m][n];
    }
}

题目三:

思路:

先理解题意,子序列就两个特点

第一:简单来说就是原数组的子集,即子序列里面的元素个数小于等于原数组的元素个数,并且元素都来自原数组

第二:与原数组的前后顺序是一样的,比如示例1中,2在101前面,那么只要子序列出现2和101,那么2必须在101之前,101必须在2之后

而题目要求的是最长递增子序列,那么先理解递增,比如示例1中,[10,9]是子序列,但是不是递增的,所以不满足条件;在所有递增的子序列中还要求最长的,那么经过列举就知道长度为4,但注意题目虽说[2,3,7,101]是最长递增子序列,但不唯一,因为同理[2,5,7,18]也是,也就是说3和5可以进行互换,101可以和18互换,两两匹对有四个最长递增子序列,但长度都为4

题意理解好了,就来解决问题

要求最长递增子序列,那么我们就枚举出所有递增子序列,再求最长的

先画决策树

 先选子序列的第一位,如决策树的第一层,然后再选子序列的第二位,如决策树的第二层,如此类推(因为要的是递增,所以不是递增的就剪枝掉了)

 所以这时就开始定义dfs的功能,根据题意要求即给dfs一个原数组的位置,要dfs返回以该位置为起点的最长递增子序列

这里其实比较抽象,画递归展开图很绕,所以就像我们之前专题说的,无条件相信dfs,它一定能实现的,宏观的来看递归

然后编写dfs,其中我们拿到起点位置,那么我们要先遍历所有后面的元素,又因为要递增,所以只有大于当前位置的元素才能用,然后用dfs拿到这个后面的元素的最长递增子序列,此时加上当前元素的长度1,与保存结果进行比较,选出最大的并更新

而结束条件这道题其实没有,因为我们在遍历后面元素时,最后会来到最后元素,而最后元素后面没有元素,就不会进入循环,也就不会进入dfs了,这时就直接返回了,而因为最后元素自己也有长度1,所以要返回1,因此结果要初始化为1,而不是0

主函数则遍历数组,看看以哪个位置为起点的递增子序列最长,然后返回最长的长度

代码(暴力):

class Solution {
    int ret;
    public int dfs(int[] nums,int cur){
        //初始化为1,因为最后一个元素时,不进循环,且长度为自身1
        int ans=1;
        //遍历后面的元素
        for(int i=cur+1;i<nums.length;i++){
            //如果是递增的
            if(nums[i]>nums[cur]){
                //选出该元素为起点的最长递增子序列加上当前长度和结果的最大值
                ans=Math.max(dfs(nums,i)+1,ans);                    
            }
        }
        return ans;
    }
    public int lengthOfLIS(int[] nums) {
        //遍历数组,看看以哪个为起点的递增子序列最长
        for(int i=0;i<nums.length;i++){
            ret=Math.max(ret,dfs(nums,i));
        }
        //返回最长的
        return ret;
    }
}

结果当然是超时了,这个更恐怖,以前还是二叉树为O(2^N),这道直接多叉树,直接是O(N^N)的时间复杂度了

这时就来想想优化

还是刚刚的决策树,多看一下就发现里面有很多重复计算,比如在2这个分支的时候,我们就算过以5为起点的递增子序列的最长长度,而决策树的第一层后面又来了5,又要重新算,所以我们就可以使用记忆化搜索,添加个备忘录

很简单,就进入dfs的时候,看一下备忘录,有就直接用,没有就老实算,算完了就保存到备忘录,再返回

代码(记忆化搜索):

class Solution {
    int ret;
    int[] memo;
    public int dfs(int[] nums,int cur){
        //如果备忘录里有
        if(memo[cur]!=0){
            return memo[cur];
        }
        //初始化为1,因为最后一个元素时,不进循环,且长度为自身1
        int ans=1;
        //遍历后面的元素
        for(int i=cur+1;i<nums.length;i++){
            //如果是递增的
            if(nums[i]>nums[cur]){
                //选出该元素为起点的最长递增子序列加上当前长度和结果的最大值
                ans=Math.max(dfs(nums,i)+1,ans);                    
            }
        }
        //添加到备忘录
        memo[cur]=ans;
        return ans;
    }
    public int lengthOfLIS(int[] nums) {
        memo=new int[nums.length];
        //遍历数组,看看以哪个为起点的递增子序列最长
        for(int i=0;i<nums.length;i++){
            ret=Math.max(ret,dfs(nums,i));
        }
        //返回最长的
        return ret;
    }
}

然后随便改成动态规划,因为我们决策树是先从下往上返回的,所以动态规划填表的顺序是从后往前来填的,因为要知道后面的,才能知道前面的

最后一个元素填表时,对应的是长度是自己,所以为1,那么dp表就全部初始化为1,对应记忆化搜索的代码就是ans=1

代码(动态规划):

class Solution {
    public int lengthOfLIS(int[] nums) {
        int ret=0;
        int[] dp=new int[nums.length];
        //初始化
        Arrays.fill(dp,1);
        //从后往前填
        for(int i=nums.length-1;i>=0;i--){
            //往后找子序列
            for(int j=i+1;j<nums.length;j++){
                //递增
                if(nums[i]<nums[j]){
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
            //更新最长的长度
            ret=Math.max(ret,dp[i]);
        }
        return ret;
    }
}

题目四:

思路:

 题意需要看懂,如果看不懂题就根本做不了了,题目会给你一个数,让你返回1—n之间猜数字所花费最少的钱且保证绝对能猜对,而其中会出现不花钱的情况,一种是猜对了,另一种是没必要猜因为答案就剩这一个了,其实本质是一样的

其中的决策树的画法我们可以采用暴力枚举,将所有可能的决策全部枚举出来,且枚举的都是最坏情况,即最后一次才能猜出来

第一层就代表第一次选择猜哪一个数,范围从1—n

第二层就代表第二次选择猜哪一个数,假如上一层选择了k,小于则是1(left)—k-1(right),大于则是k+1(left)—n(right)

如此类推枚举

有非常多的决策情况,但会有一种是最优决策,比如1—10,最优决策就是示例1演示的

所以我们暴力枚举出来就行

而有两种结束情况,在小于区间时,k==left+1时,此时k-1==left,就不用继续了,包能猜对的,直接返回花费0元,而在大于区间时,k==right或者right-1时,此时k+1>=right,也不需要花钱,返回0元即可

代码(暴力):

class Solution {
    public int getMoneyAmount(int n) {
        //给一个区间,返回所需要的最少钱
        return dfs(1,n);
    }
    public int dfs(int left,int right){
        //如果只剩下一个或者没有右区间了
        if(left>=right){
            return 0;
        }
        int ret=Integer.MAX_VALUE;
        //在left和right区间选猜哪个数需要花ret最少
        for(int i=left;i<=right;i++){
            //小于区间
            int x=dfs(left,i-1);
            //大于区间
            int y=dfs(i+1,right);
            //所有猜的数最少,而每个最少由左右区间最大的决定
            ret=Math.min(Math.max(x,y)+i,ret);
        }
        return ret;
    }
}

这里主要理解一下max和min这里,max用于决定猜的当前这个数最少需要花多少钱,min用于决定猜哪一个数花费最少

当然会超时,所以用记忆化搜索,因为会有重复的计算,比如下面这个例子

[6,10]这个区间就会重复计算,所以每次计算完一个区间,就记录该区间最少需要花多少钱

每次进去的时候就先去备忘录找一下,有就直接用,没有就算一遍,然后存到备忘录里面

因为有两个参数,所以备忘录也应该是二维的

代码(记忆化搜索):

class Solution {
    int[][] memo;
    public int getMoneyAmount(int n) {
        memo=new int[n+1][n+1];
        //给一个区间,返回所需要的最少钱
        return dfs(1,n);
    }
    public int dfs(int left,int right){
        //如果只剩下一个或者没有右区间了
        if(left>=right){
            return 0;
        }
        //查看备忘录
        if(memo[left][right]!=0){
            return memo[left][right];
        }
        int ret=Integer.MAX_VALUE;
        //在left和right区间选猜哪个数需要花ret最少
        for(int i=left;i<=right;i++){
            //小于区间
            int x=dfs(left,i-1);
            //大于区间
            int y=dfs(i+1,right);
            //所有猜的数最少,而每个最少由左右区间最大的决定
            ret=Math.min(Math.max(x,y)+i,ret);
        }
        //存进备忘录
        memo[left][right]=ret;
        return ret;
    }
}

题目五:

 思路:

跟之前走迷宫的题几乎一模一样,暴力枚举每一个点的递增路径,选最长的那个

dfs可以有返回值也可以没有返回值

那我们就暴力的时候用没有返回值的,记忆化搜索就用有返回值的,两个版本都看得到

代码(暴力+无返回值dfs):

class Solution {
    //方向向量数组
    int[] dx={0,0,1,-1};
    int[] dy={1,-1,0,0};
    int row,col,ret;
    public int longestIncreasingPath(int[][] matrix) {
        //行列
        row=matrix.length;
        col=matrix[0].length;
        //暴力遍历每个点的路径情况
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                dfs(matrix,i,j,1);
            }
        }
        //返回最长的递增路径
        return ret;
    }
    public void dfs(int[][] matrix,int i,int j,int count){
        //更新最长路径
        if(count>ret){
            ret=count;
        }
        //上下左右
        for(int k=0;k<4;k++){
            int x=i+dx[k],y=j+dy[k];
            //不越界
            if(x>=0&&x<row&&y>=0&&y<col){
                //递增
                if(matrix[i][j]<matrix[x][y]){
                    //继续遍历下一个点
                    dfs(matrix,x,y,count+1);
                }
            }
        }
    }
}

最后还是超时了,同理可以记录每一个点的最长路径,这样再次经过这个点的时候,就可以直接用了,而不需要再重复计算一次

还是老规矩,先进去看看备忘录有没有,有就用,没有就算一次,然后存进备忘录里

代码(记忆化搜索):

class Solution {
    //方向向量数组
    int[] dx={0,0,1,-1};
    int[] dy={1,-1,0,0};
    int row,col,ret;
    int[][] memo;
    public int longestIncreasingPath(int[][] matrix) {
        //行列
        row=matrix.length;
        col=matrix[0].length;
        memo=new int[row][col];
        //暴力遍历每个点的路径情况
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                ret=Math.max(dfs(matrix,i,j),ret);
            }
        }
        //返回最长的递增路径
        return ret;
    }
    public int dfs(int[][] matrix,int i,int j){
        //查看一下备忘录
        if(memo[i][j]!=0){
            return memo[i][j];
        }
        //算上当前位置路径长度为1
        int count=1;
        //上下左右
        for(int k=0;k<4;k++){
            int x=i+dx[k],y=j+dy[k];
            //不越界
            if(x>=0&&x<row&&y>=0&&y<col){
                //递增
                if(matrix[i][j]<matrix[x][y]){
                    //找到最长路径的方向
                    count=Math.max(dfs(matrix,x,y)+1,count);
                }
            }
        }
        //记录到备忘录
        memo[i][j]=count;
        //返回该点的最长路径
        return count;
    }
}

总结:

其实难的不是记忆化搜索,而是如何写出暴搜的代码,也就是dfs,如果暴搜能过就过,过不了就看看能不能用记忆化搜索来优化,不是所有的暴搜都能转记忆化搜索的,要看是否存在相同的计算,如果有,那么就添加一个备忘录,dfs进去的时候就看一看备忘录,有就用,没有就算一次,然后再保存到备忘录,几乎是个模板,难的还是dfs

综上所有递归回溯的部分就基本学完了,接下来会继续学其他的算法

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

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

相关文章

c++入门-------命名空间、缺省参数、函数重载

C系列 文章目录 C系列前言一、命名空间二、缺省参数2.1、缺省参数概念2.2、 缺省参数分类2.2.1、全缺省参数2.2.2、半缺省参数 2.3、缺省参数的特点 三、函数重载3.1、函数重载概念3.2、构成函数重载的条件3.2.1、参数类型不同3.2.2、参数个数不同3.2.3、参数类型顺序不同 前言…

51单片机测试题AI作答测试(DeepSeek Kimi)

单片机测试题 DeepSeek Kimi 单项选择题 &#xff08;10道&#xff09; 6题8题判断有误 6题判断有误 智谱清言6题靠谱&#xff0c;但仔细斟酌&#xff0c;题目出的貌似有问题&#xff0c;详见 下方。 填空题 &#xff08;9道&#xff09; 脉宽调制&#xff08;Pulse …

去耦电容的作用详解

在霍尔元件的实际应用过程中&#xff0c;经常会用到去耦电容。去耦电容是电路中装设在元件的电源端的电容&#xff0c;其作用详解如下&#xff1a; 一、基本概念 去耦电容&#xff0c;也称退耦电容&#xff0c;是把输出信号的干扰作为滤除对象。它通常安装在集成电路&#xf…

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷(二)

2024-2025 学年广东省职业院校技能大赛 “信息安全管理与评估”赛项 技能测试试卷&#xff08;二&#xff09; 第一部分&#xff1a;网络平台搭建与设备安全防护任务书第二部分&#xff1a;网络安全事件响应、数字取证调查、应用程序安全任务书任务 1&#xff1a;应急响应&…

深入剖析:基于红黑树实现自定义 map 和 set 容器

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; 在 C 标准模板库&#xff08;STL&#xff09;的大家庭里&#xff0c;map和set可是超级重要的关联容器成员呢&#x1f60e;&#x…

20-R 绘图 - 饼图

R 绘图 - 饼图 R 语言提供来大量的库来实现绘图功能。 饼图&#xff0c;或称饼状图&#xff0c;是一个划分为几个扇形的圆形统计图表&#xff0c;用于描述量、频率或百分比之间的相对关系。 R 语言使用 pie() 函数来实现饼图&#xff0c;语法格式如下&#xff1a; pie(x, l…

第438场周赛:判断操作后字符串中的数字是否相等、提取至多 K 个元素的最大总和、判断操作后字符串中的数字是否相等 Ⅱ、正方形上的点之间的最大距离

Q1、判断操作后字符串中的数字是否相等 1、题目描述 给你一个由数字组成的字符串 s 。重复执行以下操作&#xff0c;直到字符串恰好包含 两个 数字&#xff1a; 从第一个数字开始&#xff0c;对于 s 中的每一对连续数字&#xff0c;计算这两个数字的和 模 10。用计算得到的新…

软考教材重点内容 信息安全工程师 第17章 网络安全应急响应技术原理与应用

17.1 网络安全应急响应概述 网络安全应急响应是针对潜在发生的网络安全事件而采取的网络安全措施。 17.1.1 网络安全应急响应概念 网络安全应急响应是指为应对网络安全事件&#xff0c;相关人员或组织机构对网络安全事件进行监测、预警、分析、响应和恢复等工作。 17.2.3 网络安…

点击修改按钮图片显示有问题

问题可能出在表单数据的初始化上。在 ave-form.vue 中&#xff0c;我们需要处理一下从后端返回的图片数据&#xff0c;因为它们可能是 JSON 字符串格式。 vue:src/views/tools/fake-strategy/components/ave-form.vue// ... existing code ...Watch(value)watchValue(v: any) …

Node.js技术原理分析系列——Node.js的perf_hooks模块作用和用法

Node.js 是一个开源的、跨平台的 JavaScript 运行时环境&#xff0c;它允许开发者在服务器端运行 JavaScript 代码。Node.js 是基于 Chrome V8 引擎构建的&#xff0c;专为高性能、高并发的网络应用而设计&#xff0c;广泛应用于构建服务器端应用程序、网络应用、命令行工具等。…

用DeepSeek来帮助学习three.js加载3D太极模形

画一个平面的太极图是很容易&#xff0c;要实现3D的应该会很难 一、参考3D模形效果 看某网页看到一个效果&#xff0c;像一个3D太极球&#xff0c;觉得挺有趣&#xff0c;挺解压的&#xff0c;想进一步去了解下这是如何实现 效果&#xff1a; 链接地址&#xff1a; http://www.…

【JavaEE进阶】Spring Boot配置文件

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗 如有错误&#xff0c;欢迎指出~ 目录 SpringBoot配置⽂件 举例: 通过配置文件修改端口号 配置⽂件的格式 properties基本语法 读取配置⽂件 properties配置文件的缺点 yml配置⽂件 yml基本语法 yml和proper…

学习通用多层次市场非理性因素以提升股票收益预测

“Learning Universal Multi-level Market Irrationality Factors to Improve Stock Return Forecasting” 论文地址&#xff1a;https://arxiv.org/pdf/2502.04737 Github地址&#xff1a;https://github.com/lIcIIl/UMI 摘要 深度学习技术与量化交易相结合&#xff0c;在股…

【Godot4.3】基于绘图函数的矢量蒙版效果与UV换算

概述 在设计圆角容器时突发奇想&#xff1a; 将圆角矩形的每个顶点坐标除以对应圆角矩形所在Rect2的size&#xff0c;就得到了顶点对应的UV坐标。然后使用draw_colored_polygon&#xff0c;便可以做到用图片填充圆角矩形的效果。而且这种计算的效果就是图片随着其填充的图像缩…

DeepSeek开源周Day1:FlashMLA引爆AI推理性能革命!

项目地址&#xff1a;GitHub - deepseek-ai/FlashMLA 开源日历&#xff1a;2025-02-24起 每日9AM(北京时间)更新&#xff0c;持续五天&#xff01; ​ 一、开源周震撼启幕 继上周预告后&#xff0c;DeepSeek于北京时间今晨9点准时开源「FlashMLA」&#xff0c;打响开源周五连…

通过恒定带宽服务器调度改进时间敏感网络(TSN)流量整形

论文标题 英文标题&#xff1a;Improving TSN Traffic Shaping with Constant Bandwidth Server Scheduling 中文标题&#xff1a;通过恒定带宽服务器调度改进时间敏感网络&#xff08;TSN&#xff09;流量整形 作者信息 作者&#xff1a;Benjamin van Seggelen 指导教师&am…

如何查看图片的原始格式

问题描述&#xff1a;请求接口的时候&#xff0c;图片base64接口报错&#xff0c;使用图片url请求正常 排查发现是图片格式的问题&#xff1a; 扩展名可能被篡改&#xff1a;如果文件损坏或扩展名被手动修改&#xff0c;实际格式可能与显示的不同&#xff0c;需用专业工具验证…

赛前启航 | 三场重磅直播集结,予力微软 AI 开发者挑战赛!

随着微软 AI 开发者挑战赛的火热进行&#xff0c;赛前指导直播已成为众多参赛者获取技术干货、灵感碰撞和实战技巧的绝佳平台。继前两期的精彩呈现&#xff0c;第三、四、五期直播即将接连登场&#xff0c;为开发者们带来更加深入的 AI 技术剖析和项目实战指引。无论你是想进一…

VMware安装Centos 9虚拟机+设置共享文件夹+远程登录

一、安装背景 工作需要安装一台CentOS-Stream-9的机器环境&#xff0c;所以一开始的安装准备工作有&#xff1a; vmware版本&#xff1a;VMware Workstation 16 镜像版本&#xff1a;CentOS-Stream-9-latest-x86_64-dvd1.iso &#xff08;kernel-5.14.0&#xff09; …

【HarmonyOS Next】地图使用详解(一)

背景 这系列文章主要讲解鸿蒙地图的使用&#xff0c;当前可以免费使用&#xff0c;并提供了丰富的SDK给开发者去自定义控件开发。目前可以实现个性化显示地图、位置搜索和路径规划等功能&#xff0c;轻松完成地图构建工作。需要注意的是&#xff0c;现在测试只能使用实体手机去…