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

算法基础-----【动态规划】

动态规划(待完善)

动规五部曲分别为:

  1. 确定dp数组(dp table)以及下标的含义
  2. 确定递推公式(状态转移公式)
  3. dp数组如何初始化
  4. 确定遍历顺序
  5. 举例推导dp数组、

动态规划的核心就是递归+剪枝(存储键值,下次不再访问,用空间换时间)

找问题的最好方式就是把dp数组打印出来,看看究竟是不是按照自己思路推导的!
这道题目我举例推导状态转移公式了么?
我打印dp数组的日志了么?
打印出来了dp数组和我想的一样么?
或者更清晰的知道自己究竟是哪一点不明白,是状态转移不明白,还是实现代码不知道该怎么写,还是不理解遍历dp数组的顺序。

动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的,例如:有N件物品和一个最多能背重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

动态规划中dp[j]是由dp[j-weight[i]]推导出来的,然后取max(dp[j], dp[j - weight[i]] + value[i])。

但如果是贪心呢,每次拿物品选一个最大的或者最小的就完事了,和上一个状态没有关系。

所以贪心解决不了动态规划的问题。

动态规划的解题步骤:(1)确定dp数组以及下标含义 (2)确定递推公式 (3)dp数组如何初始化 (4)确定遍历顺序 (5)例举推导dp数组。

【基础题目】

【509】斐波那契数列

解题步骤:

1)确定dp数组以及下标含义

dp[i]: 第i个数的斐波那契数值是dp[i]。

2)确定递推公式

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

3)dp数组如何初始化

dp[0] = 0 dp[1] = 1

4)确定遍历顺序

从前向后遍历

5)例举推导dp数组。

按照这个递推公式dp[i] = dp[i - 1] + dp[i - 2],我们来推导一下,当N为10的时候,dp数组应该是如下的数列:

0 1 1 2 3 5 8 13 21 34 55

如果代码写出来,发现结果不对,就把dp数组打印出来看看和我们推导的数列是不是一致的。

//动态规划基础问题
/*递归的方法 :时间复杂度o(n^2) 空间复杂度o(1)*/
class Solution {
public:int fib(int n) {if(n<2)return n;return fib(n-1)+fib(n-2);}
};
/*动态规划的方法 时间复杂度o(n) 空间复杂度o(n)(经典做法)*/
class Solution {
public:int fib(int n) {if(n<2)return n;vector<int> dp(n+1);dp[0] = 0;dp[1] = 1;for(int i = 2;i<n+1;i++){dp[i] = dp[i-1]+dp[i-2];}return dp[n];}
};/*动态规划简写 时间复杂度o(n) 空间复杂度o(1)*/
class Solution {
public:int fib(int n) {if(n<2)return n;vector<int> dp(2);dp[0] = 0;dp[1] = 1;for(int i = 2;i<n+1;i++){int sum = dp[0]+dp[1];dp[0] = dp[1]; dp[1] = sum;}return dp[1];}
};

【70】爬楼梯

确定递归数列:找规律 f(n) = f(n-1)+f(n-2)
确定终值f(1) = 1 f(0) = 0
存储节点:定义数组存储节点
最标准的做法,要是还要优化空间复杂度就考虑上面的做法

class Solution {
public:int climbStairs(int n) {if(n<2)return n;//(f(1)= 1,f(2) =2)vector<int> f(n+1);f[1] =1;f[2] =2;for(int i =3;i<n+1;i++){f[i] =f[i-1]+f[i-2];}return f[n];}
};

【118】杨辉三角

注意申请数组具体那一行
注意改变数组的长度的函数resize(为了防止0出现)

class Solution {
public:vector<vector<int>> generate(int numRows) {vector<vector<int>> ret(numRows);for (int i = 0; i < numRows; ++i) {ret[i].resize(i + 1);ret[i][0] = ret[i][i] = 1;for (int j = 1; j < i; ++j) {ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];}}return ret;}
};

【背包问题】

【0-1背包】

对于面试,掌握01背包和完全背包,多重背包。
在这里插入图片描述

基础引用:对于0,1背包,就是m个物品,给定对应的重量和价值,最大容量为n,这些物品你只能选一个或者不选(01),求最大价值。
在这里插入图片描述

动态规划五部曲:

(1)确定dp数组以及下标的含义dp[i] [j ]:表示下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

(2)确定递推公式:

  • 放物品i:由dp[i - 1] [j]推出,即背包容量为j,里面不放物品i的最大价值,此时dp[i] [j]就是dp[i - 1] [j]。(其实就是当物品i的重量大于背包j的重量时,物品i无法放进背包中,所以背包内的价值依然和前面相同。)
  • 放物品i:由dp[i - 1] [j - weight[i]]推出,dp[i - 1] [j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,那么dp[i - 1] [j - weight[i]] + value[i] (物品i的价值),就是背包放物品i得到的最大价值

所以递归公式: dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i]);

(3)初始化dp数组

​ 后面的公式是根据前面来推导的,所以初始化正确了才能导致dp数组正确

​ 状态转移方程 dp[i] [j] = max(dp[i - 1] [j], dp[i - 1] [j - weight[i]] + value[i]); 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。

​ 要求出 dp[ 0 ] [ j]:也就是求种类0在不同重量下的最大价值:当j<weight[0]的时候肯定装不下,都为0.所以j从weight[0]开始初始化,都为value[0]:

在这里插入图片描述

(4)确定遍历顺序:先遍历物品,再遍历重量:

for(int i =1;i<m;i++){for(int j = 0;j<=m;j++){if(j<weight[i]){dp[i][j] = dp[i-1][j];//不放}else{dp[i][j] = max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);//放}}
}

在这里插入图片描述

(5)举例推导dp数组

image-20240429141403376
#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;
class Solution{
public:int maxSolution( vector<int>& weight,vector<int>& value,int m,int n){//确定dp数组vector<vector<int>> dp(m,vector<int>(n+1,0));//要包含一个0//初始化dp数组 dp[i-1][j] 初始化 dp[0][j]for(int j = weight[0];j<=n;j++){dp[0][j] = value[0];}for(int i =1;i<m;i++){//遍历背包种类 种类1已经初始化过了,要从2开始for(int j = weight[0];j<=n;j++){//遍历重量if(j<weight[i])dp[i][j] = dp[i-1][j];else dp[i][j]= max( dp[i-1][j-weight[i]]+value[i],dp[i-1][j]);}}cout<<dp[m-1][n]<<endl;}};int main()
{int m;//背包种类int n;//空间容量 bagweightvector<int> weight(m,0);vector<int> value(m,0);
//    cin >>m>>n;
//    for(int i =0;i<m;i++){
//        cin>> cap[i];
//    }
//    for(int i =0;i<m;i++){
//        cin>> value[i];
//    }m = 3;//背包种类n = 4;//最大容量是4weight = {1,3,4};//重量value = {15,20,20};//价值Solution s;int res = s.maxSolution(weight,value,m,n);return 0;
}

【416】分割等和子集

​ 0-1背包是可以用回溯的方式去做的,和【698】【473】都有相同的做法。

​ 给你一个 只包含正整数非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

j:容量,dp[j]最大价值,可以看到都是倒叙取最大值,最后的dp数组是:

01234567891011
01111566661011

在这里插入图片描述

class Solution {
public:bool canPartition(vector<int>& nums) {int sum =0;for(auto num:nums){sum+=num;}if(sum%2 == 1)return false;//要是不能平分直接退出int n = sum/2;vector<int> dp(n+1,0);//初始化dp数组//dp遍历for(int i =0;i<nums.size();i++){for(int j=n;j>=nums[i];j--){//特别注意这个nums[i]dp[j] = max(dp[j],dp[j-nums[i]]+nums[i]);cout<<"i:"<<i<<" dp["<<j<<"]:"<<dp[j]<<endl;}}//判断if(dp[n] == n)return true;return false;}
};

【1049】最后一块石头的重量

有一堆石头,用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。

每一回合,从中选出任意两块石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块 石头。返回此石头 最小的可能重量 。如果没有石头剩下,就返回 0

class Solution {
public:int lastStoneWeightII(vector<int>& stones) {int n = stones.size();int sum =0;for(auto item:stones){sum+=item;}int target = sum/2;vector<int> dp(target+1,0);for(int i =0;i<n;i++){for(int j = target;j>=stones[i];j--){dp[j] = max(dp[j],dp[j-stones[i]]+stones[i]);}}return sum-2*dp[target];//注意最后返回的数值}
};

【494】目标和

【17】一和零

【32】最长有效括号

给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
看到最长就可以开始考虑用动规了:
解:
(1)确定dp数组和其下标
dp[i] :到[0,i]的最长有效子串长度(一维就够了,不依赖后面的字符,不需要移动)
(2)确定状态转移方程
在这里插入图片描述

if(s[i] == ')'){if(s[i-1] =='('){dp[i] = dp[i-1]+2;} else{dp[i] = dp[i-1];}
}else{//没有子串计数dp[i] = dp[i-1];
}

【完全背包】

完全背包内层循环从头开始

【322】零钱兑换

class Solution {
public:int coinChange(vector<int>& coins, int amount) {//初始化dp数组vector<int> dp(amount+1,INT_MAX);dp[0] = 0;for(int i =0;i<coins.size();i++){for(int j = coins[i];j<=amount;j++){//遍历背包 注意初始化的位置if(dp[j-coins[i]] !=INT_MAX){// 如果dp[j - coins[i]]是初始值则跳过dp[j] = min(dp[j],dp[j-coins[i]]+1);}}}if(dp[amount] == INT_MAX) return -1;return dp[amount];}
};

遍历的过程:

以coins = [1,2,5],amount = 11为例子:

01234567891011
初始化0MMMMMMMMMMM
1(只有1)01234567891011
2(1或2)011223344556
5(1或2或5)011221223323

【139】单词拆分

class Solution {
public:bool wordBreak(string s, vector<string>& wordDict) {//存储wordDict 背包unordered_set<string> wordMap(wordDict.begin(),wordDict.end());vector<int> dp(s.size()+1,false);//dp数组dp[0]= true;//求的是排列数,有顺序,背包在外层for(int i =1;i<=s.size();i++){//遍历背包for(int j =0;j<i;j++){//遍历物品string tmp = s.substr(j,i-j);if(wordMap.find(tmp)!= wordMap.end()&&dp[j] == true){dp[i] = true;}}}return dp[s.size()];}
};

【打家劫舍】

【198】打家劫舍

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

按照五部曲进行推导

class Solution {
public:int rob(vector<int>& nums) {int n = nums.size();//确定dp数组 dp[i]存放最高金额vector<int> dp(n);if(n == 0)return 0;if(n == 1)return nums[0];if( n == 2)return max(nums[0],nums[1]);dp[0] = nums[0];dp[1] = max(nums[0],nums[1]);for(int i = 2;i<n;i++){dp[i] = max(dp[i-1],nums[i]+dp[i-2]);cout<<dp[i]<<endl;}return dp[n-1];}
};

【213】

【337】

【子序列问题】

子序列包括连续、不连续、编辑距离、回文等等…

【300】最长子序列

(1)根据返回值确定dp[i]:以nums[i]结尾的最长子序列的数组长度。

(2)状态转移方程: if(nums[i]>nums[j])dp[i] = max(dp[i],dp[j]+1);(往前对比)

(3)dp数组初始化,dp[i] = 1

(4) 确定遍历顺序,dp[i+1] = dp[i]

class Solution {
public:int lengthOfLIS(vector<int>& nums) {int res =1;if(nums.size() == 1)return 1;if(nums.size() == 0)return 0;vector<int> dp(nums.size(),1);for(int i =1;i<nums.size();i++){for(int j = 0;j<i;j++){if(nums[i]>nums[j])dp[i] = max(dp[i],dp[j]+1);}if(dp[i]>res)res = dp[i];//不一定是最后一个元素,取最长子序列}return res;}
};

【301】

【152】乘积最大子数组

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。测试用例的答案是一个 32-位 整数。

注意:这道题需要维护当前最小值,存在负数让最大值变成最小值的情况
1.确定dp数组和下标
本来是只想取dp[i]:以下标 i 结尾的连续子序列的乘积的最大值。
即可得:dp[i] = max(dp[i - 1] * nums[i], nums[i]),但是如果nums[i]是负数的话,会把最大值变化最小值。或者前面累乘的最小值会变成最大值。所以我们还要加一维去维护当前最小值:
dp[i][0]:下标为i范围内的子数组最大乘积。
dp[i][1]:下标为i范围内的子数组最小乘积。
2.确定递推公式

	if nums[i] >0dp[i][0] = max(dp[i - 1][0] * nums[i], nums[i]);dp[i][1] = min(dp[i - 1][1]* nums[i], nums[i]);else dp[i][0] = max(dp[i - 1][1] * nums[i], nums[i]);dp[i][1] = min(dp[i-1][0]*nums[i],nums[i]);

3.确定初始化dp
dp[0][1] = nums[0]
dp[0][0] = nums[0]
4.初始化顺序
从左到右,从上到下
5.例举dp数组
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。

22
63
-2-12
4-48
得到第一列最大值以后,还要去选择其中最大的值进行输出:
class Solution {
public:int maxProduct(vector<int>& nums) {vector<vector<int>> dp(nums.size(),vector<int>(2,0));int m = nums.size();dp[0][0] = nums[0];dp[0][1]= nums[0];for(int i = 1;i<m;i++){if(nums[i]>0){dp[i][0] = max(dp[i-1][0]*nums[i],nums[i]);dp[i][1] = min(dp[i-1][1]*nums[i],nums[i]); }else{dp[i][0] = max(dp[i-1][1]*nums[i],nums[i]);dp[i][1] = min(dp[i-1][0]*nums[i],nums[i]); }}int res =dp[0][0];for(int i =0;i<m;i++){res = max(dp[i][0],res);}return res;}
};

为了防止越界,可以这样做:用例[0,10,10,10,10,10,10,10,10,10,-10,10,10,10,10,10,10,10,10,10,0]

class Solution {
public:int maxProduct(vector<int>& nums) {int len = nums.size();vector<long long> dpMax(len);vector<long long> dpMin(len);dpMax[0] = nums[0];dpMin[0] = nums[0];long long maxProduct = dpMax[0];for (int i = 1; i < len; ++i) {long long tmp1 = nums[i] * dpMin[i - 1];long long tmp2 = nums[i] * dpMax[i - 1];if (tmp1 < INT_MIN) {tmp1 = INT_MIN;//钳住}if (tmp2 < INT_MIN) {tmp2 = INT_MIN;}dpMax[i] = max(static_cast<long long>(nums[i]), max(tmp1, tmp2));dpMin[i] = min(static_cast<long long>(nums[i]), min(tmp1, tmp2));maxProduct = max(maxProduct, dpMax[i]);}return static_cast<int>(maxProduct);}
};

【718】最长重复子数组

多维动态规划

与图论的区别就是多维动态规划还是需要转移方程的。图论一般就是DFS和BFS直接做。
动态规划最开始做的时候,为了便于理解,都用二维dp数组(方便理解)

【62】不同路径

题目:一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
解法1:利用深搜(类似于深搜),由于只有两个方向,可以枚举出来有多少种路径。注意题目中说机器人每次只能向下或者向右移动一步,那么其实机器人走过的路径可以抽象为一棵二叉树,而叶子节点就是终点!

class Solution {
public:int dfs(int i,int j,int m,int n){if(i>=m || j>=n)return 0;//边界条件if(i == m-1 && j ==n-1)return 1;//找到了一条路return dfs(i+1,j,m,n) + dfs(i,j+1,m,n);//返回结果,左边+右边}int uniquePaths(int m, int n) {return dfs(0,0,m,n);//从(0,0)开始}
};

假设是3*3列网格,递归路径是:

                    (0, 0)/      \(1, 0)      (0, 1)/    \      /     \(2, 0)  (1, 1) (1, 1) (0, 2)/  \   /  \   /  \   /  \超出边界  (2, 1) (2, 1) (1, 2) (1, 2)/  \   /  \   /  \   /  \超出边界 (2, 2) (2, 2) 超出边界 (2, 2)/      /     \        \终点    终点   终点     终点

但是这种方法会超时,因为这个树的深度是m+n-1,这棵树的深度其实就是m+n-1(深度按从1开始计算)。那二叉树的节点个数就是 2^(m + n - 1) - 1。可以理解深搜的算法就是遍历了整个满二叉树(其实没有遍历整个满二叉树,只是近似而已。
所以上面深搜代码的时间复杂度为O(2^(m + n - 1) - 1),可以看出,这是指数级别的时间复杂度,是非常大的。

解法2:动态规划
(1)确定dp数组以及下标的含义
dp[i][j] :[0][0]到坐标[i][j]共有dp[i][j] 条不同的路径。
(2)确定dp状态转移公式
想想dp[i][j]上一个状态是什么,分别是dp[i-1][j]和dp[i][j-1]
dp[i-1][j]表示[0][0]到坐标[i][j]共有dp[i-1][j]条不同的路径,dp[i][j-1]表示[0][0]到坐标[i][j-1]共有dp[i][j-1]条不同的路径
所以可以明确dp[i][j] = dp[i-1][j]+dp[i][j-1]
(3)dp数组如何初始化
dp[i][0] = 1,从[0][0]到[i][0]的路径只有1条
dp[0][j] = 1,从[0][0]到[0][j]的路径只有1条

for(int i =0;i<m;i++)dp[i][0]=1;
for(int j =0;j<n;j++)dp[0][j]=1;

(4)确定遍历顺序
dp[i][j] = dp[i-1][j]+dp[i][j-1],从左到右,从上到下遍历,保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值的。
(5)举例推导dp数组:按照3*3的表格为例子,则最后一个dp[m-1][n-1]就是最后返回的6条不同路径。

坐标012
0111
1123
2136
class Solution {
public:int uniquePaths(int m, int n) {vector<vector<int>> dp(m,vector<int>(n,0));//进行初始化for(int i =0;i<m;i++)dp[i][0] = 1;for(int j =0;j<n;j++)dp[0][j] = 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-1][n-1];}
};
  • 时间复杂度:O(m × n)
  • 空间复杂度:O(m × n)
    用一维滚动数组可以降低空间复杂度为O( n),但是对于不熟悉的题型还是老老实实用二维数组做吧。

【64】最小路径和

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
(1)确定dp数组以及下标含义:
dp[i][j]:从[0][0]到[i][j]的最小路径数字总和。
(2)确定递推公式:
dp[i][j]只能从dp[i-1][j]和dp[i][j-1]这两个方向获得,并且要获得最小路径数字总和。d[i][j] = grid[i][j]+min{dp[i-1][j],dp[i][j-1]}
(3)初始化dp数组
第一行和第一列都只有一种走法,则d[i][0]和dp[0][j]直接累加在一起就行。
for(int i =1;i<m;i++) dp[i][0] = grid[i][0] + dp[i-1][0];
for(int j =1;j<n;j++) dp[0][j] = grid[0][j] + dp[0][j-1];
(4)遍历顺序:
从上到下,从左到右
(5)举例dp数组:
在这里插入图片描述

坐标012
0145
1286
2687
class Solution {
public:int minPathSum(vector<vector<int>>& grid) {int m = grid.size();int n = grid[0].size();vector<vector<int>> dp(m,vector<int>(n,0));dp[0][0] = grid[0][0];for(int i =1;i<m;i++) dp[i][0] = grid[i][0] + dp[i-1][0];for(int j =1;j<n;j++) dp[0][j] = grid[0][j] + dp[0][j-1];for(int i =1; i<m;i++){for(int j =1;j<n;j++){dp[i][j] = grid[i][j] + min(dp[i][j-1], dp[i-1][j]);}}return dp[m-1][n-1];}
};

【647】回文子串

给你一个字符串 s ,请你统计并返回这个字符串中 回文子串 的数目。回文字符串 是正着读和倒过来读一样的字符串。子字符串 是字符串中的由连续字符组成的一个序列。
本题可以用双指针法做,也可以用动规做(动规的空间复杂度高一点)。
(1)确定dp数组以及下标的含义
这道题,如果dp[i] 为 下标i结尾的字符串有 dp[i]个回文串的话。很难去判断递推关系,dp[i] 和 dp[i-1] ,dp[i + 1] 看上去都没啥关系。
但是我们假设知道了一个子串是回文子串的话,比如在[i,j]内是回文子串,再去看[i-1]和[j+1]是否相等,就知道是否是回文子串,换句话说,判断一个子字符串(字符串的下表范围[i,j])是否回文,依赖于,子字符串(下表范围[i + 1, j - 1])) 是否是回文。
dp[i] [j]:表示区间范围[i,j]的子串是否回文,dp[i] [j] =true表示回文,dp[i] [j] =false表示非回文。
(2)确定dp状态转移公式
要讨论dp[i+1][j-1]的状态:

	if(s[i] == s[j]){if(j-i<=1){//a aadp[i] [j] =true;res++;}else{//a bbc aif(dp[i+1][j-1]){//子串要是回文的s才是回文dp[i] [j] =true;res++}}}else{dp[i] [j] =false;}

(3)dp数组如何初始化
dp[i][j] = false
(4)确定遍历顺序
由于这里的dp[i+1][j-1]会决定dp[i][j],所以遍历顺序应该是从下到上,从左到右。
(5)举例推导dp数组
以aaa为例子,因为[i,j]是一个区间则说明i<=j,二维数组下半部分全部是false,不需要管
| 坐标 | 0 | 1 | 2 |
| ---- | ---- | ---- | ---- |
| 0| 1 | 1 |1 |
| 1| 0 | 1 | 1 |
| 2 | 0 | 0 | 1 |
本题的难点是要把从数组区间上的[i][j]抽象到二维数组上,并且初始化顺序和递推公式都有变化。

class Solution {
public:int countSubstrings(string s) {int m  = s.size();int res =0;vector<vector<bool>> dp(m,vector<bool>(m,false));//优化:i<=j ,所以二维数组下半部分都不用遍历了for(int i = m-1;i>=0;i--){for(int j = i; j< m;j++){if(s[i] == s[j]){if(j-i<=1){//a aadp[i][j] = true;res++;}else{if(dp[i+1][j-1]){//这里是区间上的[i][j]dp[i][j] = true;res++;}}}else{dp[i][j] = false;}}} return res;}
};

时间复杂度o(n^2),空间复杂度o(n ^2)。

【5】最长回文子串

给你一个字符串 s,找到 s 中最长的 回文子串。
跟【647】差不多,如果是回文子串,就是要给一个变量maxlength去判断是不是最长的回文子串

class Solution {
public:string longestPalindrome(string s) {int m = s.size();int maxlen = 0;int left = 0;vector<vector<bool>> dp(m,vector<bool>(m,false));//从下到上,从左到右 [i,j]for(int i = m-1;i>=0;i--){for(int j = i;j < m;j++){if(s[i] == s[j]){if(j-i<=1){dp[i][j] = true;}else if(dp[i+1][j-1]){dp[i][j] = true;}}else{dp[i][j] = false;}//处理最长回文子串if(dp[i][j] && j-i+1>=maxlen){maxlen = j-i+1;left = i;}}}return s.substr(left,maxlen);}
};

【1143】最长公共子序列

与【718】最长重复子数组一起做。
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。(没有指定方向)

注意:“oxcpqrsvwf”“shmtulqrypy” 最长是qr,要是单独遍历这么做还要去存储。

本来以为可以用遍历的方式做的,但是还要考虑的是求的是**最长公共子序列,一是要最长,二是要双向。**遍历的方式就有点复杂了。
这里的子序列要求有相对顺序,可以不连续。
利用动态规划:
(1)确定dp数组以及下标的含义:
dp[i][j] :长度为[0,i]的字符串text1和[0,j]的字符串text2的最长公共子序列个数;
(2) 递推公式
主要就是两大情况: text1[i] 与 text2[j]相同,text1[i] 与 text2[j ]不相同
text1[i] 与 text2[j]相同:找到公共元素:dp[i][j] = dp[i-1][j-1]+1;
text1[i] 与 text2[j]不相同:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
(3)dp数组初始化
dp[i][j] =0;
(4)dp数组遍历顺序
从递推公式可以看,有三个方向可以推导出dp[i][j]:从左到右,从上到下
在这里插入图片描述
遇到的问题:当我想初始化的时候,第一行和第二行需要先初始化,但是他们的初始化又要单独去遍历判断赋值,不如重新给dp[i][j]意义:表示第0-i-1的子序列和0-j-1的子序列,这样的话就可以减轻我们初始化的负担:
text1[i] 与 text2[j]也要随即向前移一个,这样才能判断[0][0](下面的图可以很明确,第一行表示空字符和text2比较,肯定为0,同理第一列)。

在这里插入图片描述

class Solution {
public:int longestCommonSubsequence(string text1, string text2) {int m = text1.size();int n = text2.size();vector<vector<int>> dp(m+1,vector<int>(n+1,0));for(int i =1;i<m+1;i++){for(int j =1;j<n+1;j++){if(text1[i-1] == text2[j-1]){//从0开始判断dp[i][j] = dp[i-1][j-1] +1;}else{dp[i][j] = max(dp[i-1][j],dp[i][j-1]); }}}return dp[m][n];}};

【72】 编辑距离

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符

利用动态规划:
(1)确定dp数组以及下标的含义:
dp[i][j] :长度为[0,i-1]的字符串word1和[0,j-1]的字符串word2的编辑距离操作数。
(2) 递推公式
在确定递推公式的时候,要考虑编辑的操作

if(word1[i-1] == word2[j-1]){
//donothing dp[i][j]  = dp[i-1][j-1];
}else{
//需要编辑距离
//增加//word2添加一个元素,相当于word1删除一个元素dp[i][j]  = dp[i-1][j]+1;
//删除//word2删除一个元素dp[i][j]  = dp[i][j-1]+1;
//换dp[i][j] = dp[i-1][j-1]+1;
//只要求这三个的最小值就行了dp[i][j] = min(dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1);
}

word2添加一个元素,相当于word1删除一个元素,例如 word1 = "ad" ,word2 = "a"word1删除元素'd'word2添加一个元素'd',变成word1="a", word2="ad", 最终的操作数是一样! dp数组如下图所示意的:

            a                         a     d+-----+-----+             +-----+-----+-----+|  0  |  1  |             |  0  |  1  |  2  |+-----+-----+   ===>      +-----+-----+-----+a |  1  |  0  |           a |  1  |  0  |  1  |+-----+-----+             +-----+-----+-----+d |  2  |  1  |+-----+-----+

(3)dp数组初始化
dp[i][j] 表示以下标i-1为结尾的字符串word1,和以下标j-1为结尾的字符串word2,最近编辑距离为dp[i][j]
那么dp[i][0] 和 dp[0][j] 表示什么呢?
dp[i][0] :以下标i-1为结尾的字符串word1,和空字符串word2,最近编辑距离为dp[i][0]。
那么dp[i][0]就应该是i,对word1里的元素全部做删除操作,即:dp[i][0] = i;
同理dp[0][j] = j;
(4)dp数组遍历顺序
从递推公式可以看,有三个方向可以推导出dp[i][j]:从左到右,从上到下:

class Solution {
public:int minDistance(string word1, string word2) {int m = word1.size();int n = word2.size();vector<vector<int>> dp(m+1,vector<int>(n+1,0));//空字符,直接删除,操作数就是字符长度for (int i = 0; i <= word1.size(); i++) dp[i][0] = i;//删除word1for (int j = 0; j <= word2.size(); j++) dp[0][j] = j;//删除word2for(int i =1;i<m+1;i++){for(int j =1;j<n+1;j++){if(word1[i-1] == word2[j-1]){//什么都不做dp[i][j] = dp[i-1][j-1];}else{//进行增删换三种操作//删除word1//删除word2(增加word1)//替换word1或者word2dp[i][j] = min({dp[i-1][j]+1,dp[i][j-1]+1,dp[i-1][j-1]+1});}}}return dp[m][n];}
};

字符类的都可以考虑多构造一行一列来存放空字符。

相关文章:

算法基础-----【动态规划】

动态规划(待完善) 动规五部曲分别为&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式&#xff08;状态转移公式&#xff09;dp数组如何初始化确定遍历顺序举例推导dp数组、 动态规划的核心就是递归剪枝&#xff08;存储键值&#xff0c;…...

Java中的响应式编程与Reactor框架

Java中的响应式编程与Reactor框架 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 响应式编程&#xff08;Reactive Programming&#xff09;是一种面向数据流…...

政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署ComfyUI:功能最强大、模块化程度最高的Stable Diffusion图形用户界面和后台

目录 ComfyUI的特性介绍 开始安装 做点准备工作 在Conda虚拟环境中进行 依赖项的安装 运行 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: 零基础玩转各类开源AI项目 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&…...

匿名内部类

下面代码中&#xff0c;Person24 是一个抽象类&#xff0c;这意味着它不能被直接实例化&#xff0c;只能通过继承它的子类来实现其抽象方法。代码片段中展示了如何使用匿名内部类来实现一个抽象类的实例。 package chapter04;public class Java24_Object_匿名内部类 {public s…...

react_web自定义组件_多类型Modal_搜索栏Search

目录 一、带输入框的Modal 二、提示框Modal 三、搜索栏Search 在做项目时引入一些现成的UI组件&#xff0c;但是如果和设计图冲突太大&#xff0c;更改时很麻烦&#xff0c;如果自己写一个通用组件其实也就几十分钟或者几个小时&#xff0c;而且更具UI设计更改也比较好更改&…...

Apache Flink架构介绍

目录 一、Apache Flink架构组件栈 1.1 概述 1.2 架构图 1.3 架构分层组件说明 1.3.1 物理部署层 1.3.2 Runtime 核心层 1.3.3 API & Libraries层 二、Flink运行时架构 2.1 概述 2.2 架构图 2.3 架构角色和组件 2.3.1 Flink Clients客户端 2.3.2 JobManager 2.…...

华为HCIP Datacom H12-821 卷28

1.单选题 下面是一台路由器的部分配置,关于该部分配置描述正确的是,[HUAWEI]ip ip-prefx pl permit 10.0.192.0 8greater-equal17 less-equal 18 A、10.0.192.0/8网段内,掩码长度为18的路由会匹配到该前缀列表,匹配规则为允许 B、10.0.192.0/8网段内掩码长度为21的路…...

安装Nginx以及简单使用 —— windows系统

一、背景 Nginx是一个很强大的高性能Web和反向代理服务&#xff0c;也是一种轻量级的Web服务器&#xff0c;可以作为独立的服务器部署网站&#xff0c;应用非常广泛&#xff0c;特别是现在前后端分离的情况下。而在开发过程中&#xff0c;我们常常需要在window系统下使用Nginx作…...

【UE5.3】笔记8 添加碰撞,检测碰撞

添加碰撞 打开BP_Food,添加Box Collision组件&#xff0c;与unity类似&#xff1a; 调整Box Collision的大小到刚好包裹物体&#xff0c;通过调整缩放和盒体范围来控制大小&#xff0c;一般先调整缩放找个大概大小&#xff0c;然后调整盒体范围进行微调。 碰撞检测 添加好碰撞…...

丝滑流畅!使用kimi快速完成论文仿写

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 今天的分享&#xff0c;我们将带大家探索一种新的学术写作技巧——使用Kimi进行论文仿写。本文将深入解析如何利用Kimi的智能辅助功能&#xff0c;提高论文写作的效率和质量&#xff0c…...

【C++】认识使用string类

【C】STL中的string类 C语言中的字符串标准库中的string类string类成员变量string类的常用接口说明成员函数string(constructor构造函数)~string(destructor析构函数)默认赋值运算符重载函数 遍历string下标[ ]迭代器范围for反向迭代器 capacitysizelengthmax_sizeresizecapaci…...

如何在 Odoo 16 中对 Many2Many 字段使用 Group by

Many2many 字段与 Many2one 字段类似,因为它们在模型之间建立了新的关系。在Odoo 16中,您无法按 many2many 字段分组,因为可以使用 many2many 记录选择任何记录。当您使用 many2many 字段给出 group by 过滤器时,您将遇到断言错误。 介绍如何在 Odoo 16 中使用 Many2Many…...

PCL从理解到应用【03】KDTree 原理分析 | 案例分析 | 代码实现

前言 本文分析KDTree的原理&#xff0c;集合案例深入理解&#xff0c;同时提供源代码。 三个案例&#xff1a;K近邻搜索、半径内近邻搜索、近似最近邻搜索。方法对比&#xff0c;如下表所示&#xff1a; 特性K近邻搜索半径内近邻搜索近似最近邻搜索描述查找K个最近邻点查找指…...

Windows 11内置一键系统备份与还原 轻松替代Ghost

面对系统崩溃、恶意软件侵袭或其他不可预见因素导致的启动失败&#xff0c;Windows 7~Windows 11内置的系统映像功能能够迅速将您的系统恢复至健康状态&#xff0c;确保工作的连续性和数据的完整性。 Windows内置3种备份策略 U盘备份&#xff1a;便携且安全 打开“创建一个恢…...

leetCode-hot100-动态规划专题

动态规划 动态规划定义动态规划的核心思想动态规划的基本特征动态规划的基本思路例题322.零钱兑换53.最大子数组和72.编辑距离139.单词拆分62.不同路径63.不同路径Ⅱ64.最小路径和70.爬楼梯121.买卖股票的最佳时机152.乘积最大子数组 动态规划定义 动态规划&#xff08;Dynami…...

【算法笔记自学】入门篇(2)——算法初步

4.1排序 自己写的题解 #include <stdio.h> #include <stdlib.h>void selectSort(int A[], int n) {for(int i 0; i < n - 1; i) { // 修正索引范围int k i;for(int j i 1; j < n; j) { // 修正索引范围if(A[j] < A[k]) {k j;}}if (k ! i) { // 仅在…...

Redis基础教程(六):redis 哈希(Hash)

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…...

鸿蒙开发设备管理:【@ohos.account.appAccount (应用帐号管理)】

应用帐号管理 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 导入模…...

java项目自定义打印日志,打印请求方式,参数用时等

1.相关依赖 <!-- 私人工具包 --><dependency><groupId>cn.changeforyou</groupId><artifactId>location</artifactId><version>1.13-SNAPSHOT</version></dependency><!-- hutool工具依赖 --><dependency>…...

03:EDA的进阶使用

使用EDA设计一个38译码器电路和245放大电路 1、38译码器1.1、查看74HC138芯片数据1.2、电路设计 2、245放大电路2.1、查看数据手册2.2、设计电路 3、绘制PCB3.1、导入3.2、放置3.3、飞线3.4、特殊方式连接GND3.5、泪滴3.6、配置丝印和划分区域3.7、添加typc接口供电 1、38译码器…...

Linux/Unix系统指令:(tar压缩和解压)

tar 是一个在Linux和Unix系统中用于创建和处理归档文件的命令。 下面是tar命令的详细用法&#xff0c;包括它的所有常用选项和一些示例。 基本语法 tar [选项] [归档文件] [文件或目录]常用选项 基本操作 -c&#xff1a;创建一个新的归档文件&#xff08;create&#xff09…...

MySQL 日期和时间函数知识点总结

引言 在数据库管理和开发中&#xff0c;日期查询是一项基础且频繁使用的功能。MySQL提供了丰富的日期和时间处理函数&#xff0c;使得我们能够灵活地进行日期查询和数据处理。本文将详细介绍MySQL中关于日期查询的几个重要知识点&#xff0c;并附上具体的案例。 1. MySQL的日…...

鸿蒙登录页面及页面跳转的设计

目录 任务目标任务分析任务实施1.新建工程项目HMLogin2.设计登录页面Index.visual3.设计第二个页面SecondPage4.修改Index.ets代码5.修改SecondPage.ets代码6.运行工程 任务目标 设计一个简单的登录页面&#xff0c;要求可以将第一页的登录信息&#xff0c;传递到第二个页面&a…...

【居家养老实训室】:看中医保健在养老中的应用

本文以居家养老实训室为视角&#xff0c;深入探讨了中医保健在养老中的应用。通过对中医保健理念、常用方法以及在居家养老中的具体实践进行分析&#xff0c;阐述了其在改善老年人健康状况、提高生活质量方面的重要作用。同时&#xff0c;也指出了目前应用中存在的问题&#xf…...

【区块链+基础设施】区块链服务网络 BSN | FISCO BCOS应用案例

BSN&#xff08;Blockchain-based Service Network&#xff0c;区块链服务网络&#xff09;是一个跨云服务、跨门户、跨底层框架&#xff0c;用于部 署和运行各类区块链应用的全球性基础设施网络&#xff0c;旨在为开发者提供低成本和技术互通的区块链一站式服务。 2019 年 12…...

六、快速启动框架:SpringBoot3实战-个人版

六、快速启动框架&#xff1a;SpringBoot3实战 文章目录 六、快速启动框架&#xff1a;SpringBoot3实战一、SpringBoot3介绍1.1 SpringBoot3简介1.2 系统要求1.3 快速入门1.4 入门总结回顾复习 二、SpringBoot3配置文件2.1 统一配置管理概述2.2 属性配置文件使用2.3 YAML配置文…...

SA 注册流程

目录 1. UE开机后按照3GPP TS 38.104定义的Synchronization Raster搜索特定频点 2.UE尝试检测PSS/SSS&#xff0c;取得下行时钟同步&#xff0c;并获取小区的PCI&#xff1b;如果失败则转步骤1搜索下一个频点&#xff1b;否则继续后续步骤&#xff1b; 3.解析Mib&#xff0c;…...

图像的灰度直方图

先来认识一下灰度直方图&#xff0c;灰度直方图是图像灰度级的函数&#xff0c;用来描述每个灰度级在图像矩阵中的像素个数或者占有率。接下来使用程序实现直方图&#xff1a; 首先导入所需的程序包&#xff1a; In [ ]: import cv2 import numpy as np import matplotlib…...

软件测试面试题:Redis的五种数据结构,以及使用的场景是什么?

字符串&#xff08;Strings&#xff09;&#xff1a;简单直接&#xff0c;就像记事本一样&#xff0c;用来存储和快速访问简单的数据&#xff0c;比如缓存网页或者保存用户会话信息。 列表&#xff08;Lists&#xff09;&#xff1a;有序的数据集合&#xff0c;适合用来存储按…...

Java后端每日面试题(day1)

目录 JavaWeb三大组件依赖注入的方式Autowire和Resurce有什么区别&#xff1f;Spring Boot的优点Spring IoC是什么&#xff1f;说说Spring Aop的优点Component和Bean的区别自定义注解时使用的RetentionPolicy枚举类有哪些值&#xff1f;如何理解Spring的SPI机制&#xff1f;Spr…...

AI与测试相辅相成

AI助力软件测试 1.AI赋能软件测试 使用AI工具来帮助测试人员提高测试效率&#xff0c;提供缺陷分析和缺陷预测。 语法格式 设定角色 具体指示 上下文格式 例: 角色&#xff1a;你是一个测试人员 内容&#xff1a;请帮我生成登录案例的测试用例 ​ 1.只有输入正确账号和密码才…...

搜索+动态规划

刷题刷题刷题刷题 ​​​​​​​​​​​​​​Forgery - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 思路&#xff1a; 需要两个数组&#xff0c;一个数组全部初始化为".",另一个数组输入数据&#xff0c;每碰到一个“.”就进行染色操作&#xff0c;将其周围的…...

strcpy,srtcmp,strlen函数漏洞利用

strcpy,srtcmp,strlen函数漏洞利用 strcpy strcpy函数用于将字符串复制到另一个指针指向的空间中&#xff0c;遇到空字符 **b’x\00’**时停止&#xff0c;&#xff1a; 所以可以利用 strcpy不检查缓冲区 的漏洞&#xff08;构造的字符串要以\0结尾&#xff09;&#xff0c;…...

SketchUp + Enscape+ HTC Focus3 VR

1. 硬件: 设备连接 2. 软件: 安装steam steamVR Vive Business streaming 3. 操作: 双方登录steam 账号,然后带上头盔,用手柄在HTC Focus3 安装 串流软件,选择串流软件,在Enscape中选择 VR 模式即可 4.最终效果: SketchUp Enscape HTC Focus 3 VR 实时预览_哔哩哔哩_bi…...

推荐3款Windows系统的神级软件,免费、轻量、绝对好用!

DiskView DiskView是一款用于管理和查看磁盘空间的工具&#xff0c;它集成了于微软的Windows操作系统资源管理器中&#xff0c;以显示直观的磁盘空间使用情况。该软件通过生成图形化地图&#xff0c;帮助用户组织和管理大量文件和文件夹&#xff0c;从而高效地管理磁盘空间。用…...

-bash: /snap/bin/docker: 没有那个文件或目录

-bash: /snap/bin/docker: 没有那个文件或目录 解决办法 export PATH$PATH:/usr/bin/docker然后&#xff0c;重新加载配置文件 source ~/.bashrc...

[深度学习]卷积理解

单通道卷积 看这个的可视化就很好理解了 https://github.com/vdumoulin/conv_arithmetic/blob/master/README.md 多通道卷积 当输入有多个通道时,卷积核需要拥有相同的通道数. 假设输入有c个通道,那么卷积核的每个通道分别于相应的输入数据通道进行卷积,然后将得到的特征图对…...

基于aardio web.view2库和python playwright包的内嵌浏览器自动化操作

通过cdp协议可以实现playwright操控webview。 新建Python窗口工程 修改pip.aardio 修改pip.aardio&#xff0c;并执行&#xff0c;安装playwright。 //安装模块 import process.python.pip; //process.python.path "python.exe";/* 安装模块。 参数可以用一个字…...

《数据仓库与数据挖掘》 总复习

试卷组成 第一章图 第二章图 第三章图 第四章图 第五章图 第六章图 第九章图 第一章 DW与DM概述 &#xff08;特点、特性&#xff09; DB到DW 主要特征 &#xff08;1&#xff09;数据太多&#xff0c;信息贫乏&#xff08;Data Rich&#xff0c; Information Poor)。 &a…...

EtherCAT主站IGH-- 8 -- IGH之domain.h/c文件解析

EtherCAT主站IGH-- 8 -- IGH之domain.h/c文件解析 0 预览一 该文件功能`domain.c` 文件功能函数预览二 函数功能介绍1. `ec_domain_init`2. `ec_domain_clear`3. `ec_domain_add_fmmu_config`4. `ec_domain_add_datagram_pair`5. `ec_domain_finish`6. `ecrt_domain_reg_pdo_en…...

《昇思25天学习打卡营第10天|使用静态图加速》

文章目录 今日所学&#xff1a;一、背景介绍1. 动态图模式2. 静态图模式 三、静态图模式的使用场景四、静态图模式开启方式1. 基于装饰器的开启方式2. 基于context的开启方式 总结&#xff1a; 今日所学&#xff1a; 在上一集中&#xff0c;我学习了保存与加载的方法&#xff…...

【HarmonyOS4学习笔记】《HarmonyOS4+NEXT星河版入门到企业级实战教程》课程学习笔记(二十二)

课程地址&#xff1a; 黑马程序员HarmonyOS4NEXT星河版入门到企业级实战教程&#xff0c;一套精通鸿蒙应用开发 &#xff08;本篇笔记对应课程第 32 节&#xff09; P32《31.通知-基础通知》 基础文本类型通知&#xff1a;briefText 没有用&#xff0c;写了也白写。 长文本类型…...

六西格玛绿带培训如何告别“走过场”?落地生根

近年来&#xff0c;六西格玛绿带培训已经成为了众多企业提升管理水平和员工技能的重要途径。然而&#xff0c;不少企业在实施六西格玛绿带培训时&#xff0c;往往陷入形式主义的泥潭&#xff0c;导致培训效果大打折扣。那么&#xff0c;如何避免六西格玛绿带培训变成“走过场”…...

Linux——提取包文件到指定目录,命令解释器-shell,type 命令

- 提取包文件到指定目录 bash tar xf/-xf/-xzf 文件名.tar.gz [-C 目标路径] tar xf/-xf/-xjf 文件名.tar.bz2 [-C 目标路径] tar xf/-xf/-xJf 文件名.tar.xz [-C 目标路径] ### 示例 - 将/etc下所有内容打包压缩到/root目录中 bash [rootserver ~]# tar -cvf taretc…...

【最详细】PhotoScan(MetaShape)全流程教程

愿天下心诚士子&#xff0c;人人会PhotoScan&#xff01; 愿天下惊艳后辈&#xff0c;人人可剑开天门&#xff01; 本教程由CSDN用户CV_X.Wang撰写&#xff0c;所用数据均来自山东科技大学视觉测量研究团队&#xff0c;特此鸣谢&#xff01;盗版必究&#xff01; 一、引子 Ph…...

Excel多表格合并

我这里一共有25张表格: 所有表的表头和格式都一样,但是内容不一样: 现在我要做的是把所有表格的内容合并到一起,研究了一下发现WPS的这项功能要开会员的,本来想用代码撸出来的,但是后来想想还是找其他办法,后来找到"易用宝"这个插件,这个插件可以从如下地址下载:ht…...

AI作画工具深度剖析:Midjourney vs. Stable Diffusion (SD)

在人工智能技术的推动下&#xff0c;艺术创作的边界被不断拓宽&#xff0c;AI作画工具成为数字艺术家与创意人士的新宠。其中&#xff0c;Midjourney与Stable Diffusion&#xff08;SD&#xff09;作为当前领域的佼佼者&#xff0c;以其独特的算法机制、丰富的功能特性及高质量…...

ASP.NET Core Blazor 5:Blazor表单和数据

本章将描述 Blazor 为处理 HTML 表单提供的特性&#xff0c;包括对数据验证的支持。 1 准备工作 继续使用上一章项目。   创建 Blazor/Forms 文件夹并添加一个名为 EmptyLayout.razor 的 Razor 组件。本章使用这个组件作为主要的布局。 inherits LayoutComponentBase<div …...

C++ 仿QT信号槽二

// 实现原理 // 每个signal映射到bitset位&#xff0c;全集 // 每个slot做为signal的bitset子集 // signal全集触发&#xff0c;标志位有效 // flip将触发事件队列前置 // slot检测智能指针全集触发的标志位&#xff0c;主动运行子集绑定的函数 // 下一帧对bitset全集进行触发清…...

联合概率密度函数

目录 1. 什么是概率密度由联合概率密度求概率参考链接 1. 什么是概率密度 概率密度到底在表达什么&#xff1f; 外卖在20-40分钟内送达的概率 随机变量落在[20,40]之间的概率。下图中&#xff0c;对总面积做规范化处理&#xff0c;令总面积1&#xff0c; f ( x ) f(x) f(x)则成…...