动态规划-part1

news2024/12/19 12:02:17

目录

动态规划的基本流程:

状态表示

状态转移方程

初始化

填表顺序

返回值

空间优化:

斐波那契数列模型问题

1、第N个泰波那契数

2、三步问题

3、使用最小花费爬楼梯

4.解码方法

路径问题

 1、不同路径1

2、不同路径2

3.礼物的最大价值

4.下降路径最小和

5.最小路径和

6.地下城游戏(难度upup)


动态规划的基本流程:

状态表示

        动态规划基本都是要定义一个数组,一维或二维,我们通过某种方式将数组填满,数组的某一个值就是我们需要的答案。比如dp数组

而数组的任意一个值所代表的含义,就是状态表示 

怎么看出状态表示?

1、题目要求中,会有关键信息,很有可能就是正确的状态表示

2、经验+题目要求,多刷题结合题目要求。

3、要学会分析问题,从问题中找到子问题。

这三个层次基本就是对动态规划的不同熟练程度了。

状态转移方程

上面说了,我们要将数组填满,那怎么填呢,也就是令任意的dp[i]等于什么值呢?

状态转移,从字面意义上,我们可以理解为将状态进行了转移,对应到dp数组,就是dp[i]等于dp的任意一个或多个不同的数组值在经历了一定的变换后的结果,简单的就是比如全部累加,或者平均值等等。

初始化

上面的状态转移中,我们知道dp[i]是1个或多个值经历一定变化后的结果。我们感性的想想,数组最初开辟出来都是0,那不管怎么操作,大概率每个位置还是0吧,那这时候,我们就必须将数组的一些位置手动填上数字,一方面让整个流程跑起来,另一方面,就是以免中途遇到一些没有初始化的值,导致最后的结果变得莫名其妙了。

填表顺序

填当前dp[i]的时候,我们要保证用到的其他状态,已经是填过的或者已经初始化过的,这样才能让结果正确

返回值

题目要求+状态表示。

通过这两者,将dp数组中代表答案的输出即可,比如dp[n]

空间优化:

主要是节省空间的使用。

斐波那契数列模型问题

1、第N个泰波那契数

1137. 第 N 个泰波那契数 - 力扣(LeetCode)

 状态表示:

根据题目要求,dp[i]代表第i个泰波那契数

状态转移方程:

dp[i]=dp[i-3]+dp[i-2]+dp[i-1]

初始化:

dp[0]=0,dp[1]=dp[2]=1

填表顺序:

从下标3开始

返回值:

dp[n],第n个泰波那契数

class Solution {
public:
    int tribonacci(int n) {
        vector<int>mp(n+1,0);
        if(n==0)return 0;
        if(n==1||n==2)return 1;
        mp[0]=0,mp[1]=1,mp[2]=1;
        for(int i=3;i<=n;i++)
        {
            mp[i]=mp[i-3]+mp[i-2]+mp[i-1];
        }
        return mp[n];
    }
};

下面是节省空间的写法。

用滚动数组,只用开4位。

class Solution {
public:
    int tribonacci(int n) {
        int mp[4];
        if(n==0)return 0;
        if(n==1||n==2)return 1;
        mp[0]=0,mp[1]=1,mp[2]=1;
        for(int i=3;i<=n;i++)
        {
            int x=3;
            mp[x]=mp[x-3]+mp[x-2]+mp[x-1];
            mp[0]=mp[1];
            mp[1]=mp[2];
            mp[2]=mp[3];
        }
        return mp[3];
    }
};

2、三步问题

面试题 08.01. 三步问题 - 力扣(LeetCode)

 状态表示:

根据题目要求,dp[i]代表台阶[i]一共有多少种方法跳上来

状态转移方程:

dp[i]=dp[i-3]+dp[i-2]+dp[i-1]

初始化:

dp[1]=1,dp[2]=2,dp[3]=4

填表顺序:

从下标4开始

返回值:

dp[n],第n个台阶的方法总数

class Solution {
public:
    int waysToStep(int n) {
        vector<int>mp(n+4,0);
        mp[1]=1;
        mp[2]=2;
        mp[3]=4;
        for(int i=4;i<=n;i++)
        {
            if(i-3>=1)mp[i]+=mp[i-3];
            mp[i]%=1000000007;
            if(i-2>=1)mp[i]+=mp[i-2];
            mp[i]%=1000000007;
            if(i-1>=1)mp[i]+=mp[i-1];
            mp[i]%=1000000007;
        }
        return mp[n];
    }
};

滚动数组优化

class Solution {
public:
    int waysToStep(int n) {
        vector<int>mp(5,0);
        mp[1]=1;
        mp[2]=2;
        mp[3]=4;
        if(n==1)return 1;
        if(n==2)return 2;
        if(n==3)return 4;
        const int mod=1000000007;
        for(int i=4;i<=n;i++)
        {
            mp[4]=((mp[1]+mp[2])%mod+mp[3])%mod;
            mp[1]=mp[2];
            mp[2]=mp[3];
            mp[3]=mp[4];
        }
        return mp[4];
    }
};

3、使用最小花费爬楼梯

746. 使用最小花费爬楼梯 - 力扣(LeetCode)

思路1:

 状态表示:

根据题目要求,dp[i]代表跳到台阶i的最小花费

状态转移方程:

dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2])

初始化:

dp[0]=dp[1]=0

填表顺序:

从下标2开始

返回值:

dp[n],跳到楼顶的最小花费

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n=cost.size();
        vector<int>dp(n+1,0);
        dp[0]=0;
        dp[1]=0;
        for(int i=2;i<=n;i++)
        {
            dp[i]=min(dp[i-1]+cost[i-1],dp[i-2]+cost[i-2]);
        }
        return dp[n];
    }
};

思路2:

 状态表示:

根据题目要求,dp[i]代表从[i]台阶到楼顶的最小花费

状态转移方程:

dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i])

初始化:

dp[n]=0,dp[n-1]=cost[n-1];

填表顺序:

从下标n-2开始,从右往左

返回值:

min(dp[0],dp[1])跳到楼顶的最小花费

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int n=cost.size();
        vector<int>dp(n+1,0);
        dp[n-1]=cost[n-1];
        dp[n]=0;
        for(int i=n-2;i>=0;i--)
        {
            dp[i]=min(dp[i+1]+cost[i],dp[i+2]+cost[i]);
        }
        return  min(dp[0],dp[1]);
    }
};

4.解码方法

状态表示:

根据题目要求,dp[i]代表以i为结尾的方法总数

状态转移方程:

if(s[i]!='0')dp[i]=dp[i-1];//如果当前字符不是0的话,说明可以在前面i-1个字符解码方法的基础上再加个当前字符即可,所以直接赋值

 if(s[i-1]!='0'&&(s[i-1]-'0')*10+s[i]-'0'<=26)//如果前一个不是0(有前导0,不能合并解码),并且下标i-1字符和i字符组成的数小于等于26,说明可以合并解码,让dp[i]+=dp[i-2],也就是在前i-2个字符的基础上加上这个合并解码,所以加的方法数就是dp[i-2]

 {

                if(i-2>=0)//越界了,那说明是前2个字符触发这个了,那就是再加1

                dp[i]+=dp[i-2];

                else dp[i]+=1;

 }

初始化:

dp[0]=1

填表顺序:

从下标1开始,从左往右

返回值:

dp[s.size()-1],以最后一个字符为结尾的解码方法总数

class Solution {
public:
    int numDecodings(string s) {
        if(s[0]=='0')return 0;//如果第一个就是0,那直接返回0即可
        dp[0]=1;//第一个肯定不是0,那肯定可以单独解码,也就是1
        for(int i=1;i<s.size();i++)
        {
            if(s[i]!='0')
            dp[i]=dp[i-1];
            if(s[i-1]!='0'&&(s[i-1]-'0')*10+s[i]-'0'<=26)
            {
                if(i-2>=0)
                dp[i]+=dp[i-2];
                else dp[i]+=1;
            }
        }
        return dp[s.size()-1];
    }
    int dp[101];
};

其实,也可以让下标映射整体往后挪一位,然后让下标0存1,这样就不用特意判断i-2>=0了

路径问题

 1、不同路径1

62. 不同路径 - 力扣(LeetCode)

这个在我记忆化搜索里其实写过。

状态表示:

根据题目要求,dp[i][j]代表走到[i][j]位置的不同路径和

状态转移方程:

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

初始化:

dp[1][1]=1,并且i、j其中任意一个为0的时候,都是0,这是一种虚拟节点,保证状态转移方程在‘越界’的时候结果依旧正确

填表顺序:

从[1][1]位置开始一行行遍历,每行从左往右

返回值:

dp[m][n],即到[m][n]位置的不同路径和

class Solution {
public:
    int uniquePaths(int m, int n) {
        f[1][1]=1;
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==1&&j==1)continue;
                f[i][j]=f[i-1][j]+f[i][j-1];
            }
        }
        return f[m][n];
    }
    long long f[101][101];

};

2、不同路径2

63. 不同路径 II - 力扣(LeetCode)

比上一题多了障碍物的概念

状态表示:

根据题目要求,dp[i][j]代表走到[i][j]位置的不同路径和

状态转移方程:

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

初始化:

dp[0][0]=obstacleGrid[0][0]==1?0:1;

填表顺序:

从[0][0]位置开始一行行遍历,每行从左往右

返回值:

dp[m][n],即到[m][n]位置的不同路径和

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        dp[0][0]=obstacleGrid[0][0]==1?0:1;
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(j==0&&i==0)continue;
                if(obstacleGrid[i][j]==1)continue;
                if(i-1>=0)dp[i][j]+=dp[i-1][j];
                if(j-1>=0)dp[i][j]+=dp[i][j-1];
            }
        }
        return dp[m-1][n-1];
    }
    int dp[101][101];
};

因为从[0][0]开始走,所以要注意下标越界问题,另外如果起始位置有障碍物,方法是0的,其实这时候可以直接返回0的,但我为了整体代码美观,就没写。

在遍历过程中,如果当前位置有障碍物,那就是0的,直接continue掉即可。

这个代码还可以把下标映射整体往后挪一位,这样不用写这些边界if判断

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        dp[1][0]=1;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                if(obstacleGrid[i-1][j-1]==0)
                    dp[i][j]=dp[i-1][j]+dp[i][j-1];
        return dp[m][n];
    }
    int dp[101][101];
};

这个看起来更加简洁

3.礼物的最大价值

礼物的最大价值_牛客题霸_牛客网 (nowcoder.com)

这题前面两题的最大区别就是,本题的每个位置都有一个价值

状态表示:

根据题目要求,mp[i][j]代表移动到[i][j]位置的最大礼物价值

状态转移方程:

mp[i][j]=grid[i-1][j-1]+max(mp[i-1][j],mp[i][j-1]);

初始化:

无需特意初始化,只要把二维数组多开一圈,依靠默认的0即可,即凡是下标含0的,值都为0

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i][j-1]的位置的值比当前位置早处理即可

返回值:

mp[n][m],即到[n][m]位置的最大礼物价值

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param grid int整型vector<vector<>> 
     * @return int整型
     */
    int maxValue(vector<vector<int> >& grid) {
        int n=grid.size(),m=grid[0].size();
        vector<vector<int>>mp(n+1,vector<int>(m+1));
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                mp[i][j]=grid[i-1][j-1]+max(mp[i-1][j],mp[i][j-1]);
            }
        }
        return mp[n][m];
    }
};

4.下降路径最小和

931. 下降路径最小和 - 力扣(LeetCode)

本质跟前面几题都是一样的,只是路径的选择稍稍有变化

状态表示:

根据题目要求,mp[i][j]代表移动到[i][j]位置的最小下降路径和

状态转移方程:

mp[i][j]=matrix[i-1][j-1]+min(min(mp[i-1][j],mp[i-1][j-1]),mp[i-1][j+1]);

初始化:

数组先开大一点,比如n+3或者n+2也行,主要是防止越界

数组初始化设为一个很大的值(因为是最小值,所以如果访问到了边界,这些值默认都应该为很大,这样才不会影响正常的路径选择)

[0][1~n+1]都要初始化为0,如果不初始化为0,[0][1~n+1]会被上面所设的很大的值影响,导致结果会被放大很多。

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i-1][j-1]、[i-1][j+1]的位置的值比当前位置早处理即可

返回值:

当i遍历到最后一行,提前设置变量,对最后一行的每一个结果,进行比较,返回最小值即可

class Solution {
public:
    int minFallingPathSum(vector<vector<int>>& matrix) {
        int n=matrix.size();
        vector<vector<int>>mp(n+2,vector<int>(n+2,9999999999));
        for(int i=0;i<=n+1;i++)
        {
            mp[0][i]=0;
        }
        int ans=9999999999;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                mp[i][j]=matrix[i-1][j-1]+min(min(mp[i-1][j],mp[i-1][j-1]),mp[i-1][j+1]);
                if(i==n)
                {
                    ans=min(ans,mp[i][j]);
                }
            }
        }
        return ans;
    }
};

5.最小路径和

64. 最小路径和 - 力扣(LeetCode)

思路没有太多区别,可以用动归解决的路径问题,在转移方程上基本都是大同小异,核心变化反而是对于初始化的问题

状态表示:

根据题目要求,dp[i][j]代表移动到[i][j]位置的最小下降路径和

状态转移方程:

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

初始化:

数组先开n+1

数组初始化设为一个很大的值(因为是最小值,所以如果访问到了边界,这些值默认都应该为很大,这样才不会影响正常的路径选择)

因为对于[i][j]来说,只能从[i][j-1]和[i-1][j]走过来,所以要保证边界值不影响[i][j]的判断,对于第1行来说,他们只能从左边获取值,不能从上面获取值,而由于上面默认都设置了大值,导致[1][1]的位置无法正常设置,因为对于[1][1]来说,[0][1]和[1][0]都是一个大值,选哪一个都会导致结果异常,所以[1][1]必须提前设置,并且后续不能循坏到[1][1]

填表顺序:

从[1][1]开始遍历,逐行变量,每行都从左往右,保证[i-1][j]和[i][j-1]的位置的值比当前位置早处理即可

返回值:

返回dp[m][n]即可,即到达[m][n]位置的最小路径和

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int m=grid.size();
        int n=grid[0].size();
        vector<vector<int>>dp(m+1,vector<int>(n+1,INT_MAX));
        dp[1][1]=grid[0][0];
        for(int i=1;i<=m;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(i==1&&j==1)continue;
                dp[i][j]=min(dp[i][j-1],dp[i-1][j])+grid[i-1][j-1];
            }
        }
        return dp[m][n];
    }
};

6.地下城游戏(难度upup)

174. 地下城游戏 - 力扣(LeetCode)

虽然同样是路径问题,但是在状态表示上有很大的不同。

状态表示:

根据题目要求,dp[i][j]代表进入[i][j]前,并且一定以[i][j]为起点,移动到[m][n]位置的最小健康值

注意,这跟前面有很大的区别,前面基本都是移动到[i][j],以[i][j]结尾,而这里是以[i][j]开始。

之所以这样,是因为本题后面的值,是会影响最初的健康值的。简而言之,要以不变往变走。

状态转移方程:

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

假设dp[i][j]==x,dp[i-1][j+1]==y,dungeon[i-1][j-1]==mp[i-1][j-1],那么dp[i][j+1]只可能是x+mp[i-1][j-1]或y+mp[i-1][j-1],因此x>=dp[i][j+1]-mp[i-1][j-1],同理x>=dp[i+1][j]-mp[i-1][j-1];所以x==min(dp[i+1][j],dp[i][j+1])-dungeon[i-1][j-1]

dp[i][j]=max(1,dp[i][j]); 

然而考虑到一个问题,那就是mp[i-1][j-1]可能大于min(dp[i+1][j],dp[i][j+1]),也就是某个房间加了大量的健康值,有人可能疑问为什么,因为在我们这个dp的第一个关系式中,每一个位置的关系是取决于后面房间的,也就是说,前一个房间和当前房间是没有关系的,这样的话mp[i-1][j-1]就完全有可能大于min(dp[i+1][j],dp[i][j+1]),因此遇到这种情况,说明只要以最小正整数1进入[i][j]位置,就可以保证后面可以走到[m][n]

初始化:

初始开[m+2][n+2]

全部填大值,然后让dp[m][n]=dungeon[m-1][n-1]>0?1:abs(dungeon[m-1][n-1])+1;

填表顺序:

从[m][n-1]开始遍历,逐行变量,每行都从右往左,保证[i+1][j]和[i][j+1]的位置的值比当前位置早处理即可

返回值:

返回dp[1][1]即可,即进入[1][1]前,并且一定以[1][1]为起点,移动到[m][n]位置的最小健康值

class Solution {
public:
    int calculateMinimumHP(vector<vector<int>>& dungeon) {
        int m=dungeon.size();
        int n=dungeon[0].size();
        vector<vector<int >>dp(m+2,vector<int>(n+2,INT_MAX));
        dp[m][n]=dungeon[m-1][n-1]>0?1:abs(dungeon[m-1][n-1])+1;
        for(int i=m;i>=1;i--)
        {
            for(int j=n;j>=1;j--)
            {
                if(i==m&&j==n)continue;
                dp[i][j]=min(dp[i+1][j],dp[i][j+1])-dungeon[i-1][j-1];
                dp[i][j]=max(1,dp[i][j]);
            }
        }
        return dp[1][1];
    }
};

本题难度是有的,主要是状态表示和状态转移方程上面有一定难度。

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

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

相关文章

基于quasar,只选择年度与月份的组件

为什么要做 quasar是个基于vue的强大的UI开发库&#xff0c;它提供了非常多的组件&#xff0c;比如日期选择。但是有些时候只需要选择到月份就可以了&#xff0c;quasar中没有&#xff0c;所以自己动手写了一个。因为对界面编程我不熟悉&#xff0c;所以&#xff0c;如果你有更…

02-3.python入门基础一操作符与表达式

接上章 : 02-2.python入门语法一变量与数据类型2 本文将深入介绍Python中的各种操作符&#xff0c;包括算术操作符、比较操作符、逻辑操作符等&#xff0c;并详细讲解如何使用这些操作符构建表达式。通过丰富的示例与详细的讲解&#xff0c;帮助读者全面掌握这一重要的基础知识…

【自动化】Python SeleniumUtil 工具 开启开发者模式 自动安装油猴用户脚本等

【自动化】Python SeleniumUtil 工具 【Python】使用Selenium 操作浏览器 自动化测试 记录-CSDN博客文章浏览阅读58次。文章浏览阅读42次。【附件】Selenium chromedriver 驱动及浏览器下载。【附件】Selenium chromedriver 驱动及浏览器下载-CSDN博客。3.安装Chrome浏览器驱动…

COMSOL快捷键及内置函数

文章目录 COMSOL快捷键使用COMSOL算子求最大值和最小值COMSOL内置函数3.1 解析函数3.2 插值函数3.3 分段函数3.4 高斯脉冲函数3.5 斜坡函数3.6 矩形函数3.7 波形函数3.8 随机函数3.9 Matlab函数3.10 SWITCH函数 COMSOL快捷键 Ctrl&#xff0b;/ 可快速打开预定义的物理量列表。…

QT绘制同心扇形

void ChartForm::paintEvent(QPaintEvent *) {QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);// 设置抗锯齿painter.save();// 设置无边框&#xff08;不需要设置QPen&#xff0c;因为默认是不绘制边框的&#xff09;QPen pen(Qt::NoPen);// QPen pen…

最大质因子序列

最大质因子序列 C语言代码C 代码Java代码Python代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 任意输入两个正整数m, n (1 < m < n < 5000)&#xff0c;依次输出m到n之间每个数的最大质因子&#xff08;包括m和n&#xff1b;…

ubuntu22.04编译安装Opencv4.8.0+Opencv-contrib4.8.0教程

本章教程,主要记录在Ubuntu22.04版本系统上编译安装安装Opencv4.8.0+Opencv-contrib4.8.0的具体过程。 一、下载opencv和opencv-contrib包 wget https://github.com/opencv/opencv/archive/refs/tags/4.8.0.zip wget https://github.com/opencv/opencv_contrib/archive/refs/…

AI芯片常见概念

文章目录 AI芯片常见概念前言常见概念AI芯片分类按照芯片的技术架构分GPU半定制化的 FPGA全定制化 ASIC神经拟态芯片 按应用场景分训练卡推理卡 按部署位置分国产AI卡资料汇总 封装相关Chiplet技术3DIC三星多芯片集成联盟&#xff08;Samsung Multi-Die Integration Alliance&a…

Fiddler(抓包测试工具)下载安装步骤

目录 介绍 主要功能&#xff1a; 使用场景&#xff1a; 一、下载 二、安装 ​编辑三、测试 介绍 Fiddler 是一个强大的网络调试工具&#xff0c;用于捕获和分析 HTTP/HTTPS 请求与响应。它通过代理服务器捕获流量&#xff0c;帮助开发者调试 Web 应用、API&#xff0c;进…

Elasticsearch-DSL高级查询操作

一、禁用元数据和过滤数据 1、禁用元数据_source GET product/_search {"_source": false, "query": {"match_all": {}} }查询结果不显示元数据 禁用之前: {"took" : 0,"timed_out" : false,"_shards" : {&quo…

gorm源码解析(四):事务,预编译

文章目录 前言事务自己控制事务用 Transaction方法包装事务 预编译事务结合预编译总结 前言 前几篇文章介绍gorm的整体设计&#xff0c;增删改查的具体实现流程。本文将聚焦与事务和预编译部分 事务 自己控制事务 用gorm框架&#xff0c;可以自己控制事务的Begin&#xff0…

什么是双声道立体声环绕声全景声 | 一文讲清楚沉浸式声音基本设定

目录 一、 沉浸式声音基本概念1. 声学上的沉浸式2. 空间音频技术3. 声源位置4. 人耳声音定位&#xff08;水平&垂直方向&#xff09;5. 人耳对声源距离定位的影响因素6. 头部相关传递函数7. 三维声技术8. “双耳”与“立体声”9. 耳机重放与扬声器重放10. 环绕声11. 高度声…

使用C语言库函数格式化输入时格式类型与数据类型不匹配导致程序异常

问题 使用两次sscanf()库函数从两个字符串中按照指定的格式读取数据&#xff0c;执行完毕后发现第一个正常读取的数据被篡改。项目在Ubuntu上使用CMake和Ninja构建项目&#xff0c;编译时没有错误和警告。 复现 为方便调试&#xff0c;在keil中编译stm32工程代替&#xff0c…

车牌识别之三:检测+识别的onnx部署(免费下载高精度onnx模型)

依赖 paddle2onnx1.3.1 onnxruntime-gpu1.14.0 ultralytics8.3.38背景 在车牌识别之一&#xff1a;车牌检测(包含全部免费的数据集、源码和模型下载&#xff09;我们得到了车牌检测模型&#xff1b; 在车牌识别之二&#xff1a;车牌OCR识别(包含全部免费的数据集、源码和模型…

WPF ControlTemplate 控件模板

区别于 DataTemplate 数据模板&#xff0c;ControlTemplate 是控件模板&#xff0c;是为自定义控件的 Template 属性服务的&#xff0c;Template 属性类型就是 ControlTemplate。 演示&#xff0c; 自定义一个控件 MyControl&#xff0c;包含一个字符串类型的依赖属性。 pub…

在IDE中使用Git

我们在开发的时候肯定是经常使用IDE进行开发的&#xff0c;所以在IDE中使用Git也是非常常用的&#xff0c;接下来以IDEA为例&#xff0c;其他的VS code &#xff0c;Pycharm等IDE都是一样的。 在IDEA中配置Git 1.打开IDEA 2.点击setting 3.直接搜索git 如果已经安装了会自…

Excel中如何消除“长短款”

函数微调可以可以实施&#xff0c;简单且易于操作的气球&#x1f388;涨缩更妙。 (笔记模板由python脚本于2024年12月17日 06:19:13创建&#xff0c;本篇笔记适合用Excel操作数据的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Fre…

微命令 微指令 微程序 微操作

微命令是计算机控制部件通过控制线向执行部件发出的各种控制命令&#xff0c;它是构成控制序列的最小单位 微命令与微操作是一一对应的关系&#xff0c;微命令是微操作的控制信号&#xff0c;而微操作是微命令的执行过程。在机器的一个CPU周期中&#xff0c;一组实现一定操作功…

Spring 不推荐使用@Autowired

Spring 不推荐使用Autowired 原因&#xff1a;为什么 Spring和IDEA 都不推荐使用 Autowired 注解_autowired为什么不推荐-CSDN博客 解决方法&#xff1a; 使用Resource注解。 使用构造函数注入。缺点显而易见&#xff0c;当成员变量很多时&#xff0c;构造函数代码冗长&#…

6、AI测试辅助-测试报告编写(生成Bug分析柱状图)

AI测试辅助-测试报告编写&#xff08;生成Bug分析柱状图&#xff09; 一、测试报告1. 创建测试报告2. 报告补充优化2.1 Bug图表分析 3. 风险评估 总结 一、测试报告 测试报告内容应该包含&#xff1a; 1、测试结论 2、测试执行情况 3、测试bug结果分析 4、风险评估 5、改进措施…