详解动态规划算法
动态规划
- 一、动态规划的核心思想
- 二、动态规划的步骤
- 1. 定义状态(State)
- 2. 确定状态转移方程(State Transition Equation)
- 3. 确定边界条件(Base Case)
- 4. 填表(Table Filling)或递归计算
- 三、动态规划的两种常见实现方式
- 1. 自顶向下(备忘录法)
- 2. 自底向上(迭代法)
- 四、动态规划的经典应用
- 1. 背包问题
- 问题描述:
- 动态规划解法:
- 状态转移方程:
- 初始化:
- 时间复杂度:
- 代码实现:
- 代码解释:
- 解释:
- 其他优化:
- 2. 最长公共子序列(LCS)
- 问题描述:
- 动态规划解法:
- 1. 状态定义:
- 2. 状态转移方程:
- 3. 边界条件:
- 4. 最终结果:
- 动态规划的空间优化:
- 代码实现:
- 代码解析:
- 示例分析:
- 时间和空间复杂度:
- 五、动态规划的优化技巧
- 六、总结
一、动态规划的核心思想
动态规划的核心思想是将复杂问题分解为简单的子问题,并通过存储子问题的解来避免重复计算,从而节省计算时间。动态规划通常适用于满足以下两个条件的问题:
- 最优子结构(Optimal Substructure):一个问题的最优解可以由其子问题的最优解构成。
- 重叠子问题(Overlapping Subproblems):问题可以分解为许多重复的子问题,而这些子问题的解会被多次利用。
二、动态规划的步骤
解决动态规划问题时,通常遵循以下步骤:
1. 定义状态(State)
首先,我们需要定义一个状态,用来描述问题的子结构。在实际操作中我们通常会定义一个数组或表格来存储子问题的结果。要注意状态的定义应该尽量简单,但又能包含足够的信息来从中推导出最终的解。
2. 确定状态转移方程(State Transition Equation)
状态转移方程是解决动态规划问题的关键,它描述了如何从一个子问题的解推导出更大问题的解。状态转移方程通常是递推公式,能够根据已解决的子问题来构造当前问题的解。
3. 确定边界条件(Base Case)
对于最小的子问题(也就是递归的基础情形),需要明确给出其解,这些就是边界条件。在实现时,这些边界条件是动态规划算法的起点,通常是一些初始值。。
4. 填表(Table Filling)或递归计算
- 自底向上:通过循环填充表格(通常是二维数组或一维数组),从最小的子问题开始,逐步计算出最终的解。
- 自顶向下:通过递归调用子问题,并使用备忘录(Memoization)存储已计算的结果,避免重复计算。
三、动态规划的两种常见实现方式
1. 自顶向下(备忘录法)
在自顶向下的方法中,我们通过递归来解决问题,每当遇到一个子问题时,首先检查它是否已经被计算过。如果已经计算过,则直接返回存储的结果;如果没有计算过,则进行递归计算,并将结果存储起来,以便以后使用。
# 定义一个计算斐波那契数列的函数,使用记忆化递归优化性能
def fibonacci(n, memo={}):# 如果 n 已经在 memo 字典中,直接返回 memo[n],避免重复计算if n in memo:return memo[n]# 基本情况:斐波那契数列的前两项分别是 0 和 1if n <= 1:return n# 递归计算第 n 项的值:# 第 n 项等于第 (n-1) 项和第 (n-2) 项的和# 并将结果存储到 memo 字典中,供后续调用时直接使用memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)# 返回计算结果return memo[n]# 调用函数计算斐波那契数列的第 10 项
print(fibonacci(10)) # 输出 55
这个例子使用了递归,且用 memo
字典存储已经计算过的斐波那契数。
2. 自底向上(迭代法)
自底向上的方法通常通过迭代来逐步填充动态规划表格。首先解决最小的子问题,然后逐步解决更大的子问题,直到最后得到原问题的解。
# 定义一个计算斐波那契数列的函数,使用动态规划(DP)实现
def fibonacci(n):# 基本情况:如果 n 小于等于 1,直接返回 n# 因为斐波那契数列的第 0 项是 0,第 1 项是 1if n <= 1:return n# 初始化一个长度为 (n+1) 的列表 dp,用于存储斐波那契数列的每一项# dp[i] 表示斐波那契数列的第 i 项dp = [0] * (n + 1)# 显式设置斐波那契数列的第 1 项为 1dp[1] = 1# 使用循环从第 2 项开始计算到第 n 项# 每一项 dp[i] 等于前两项 dp[i-1] 和 dp[i-2] 的和for i in range(2, n + 1):dp[i] = dp[i - 1] + dp[i - 2]# 返回斐波那契数列的第 n 项return dp[n]# 调用函数计算斐波那契数列的第 10 项
print(fibonacci(10)) # 输出 55
这里使用一个一维数组 dp
存储每个斐波那契数,从 dp[0]
到 dp[n]
,并通过迭代计算出最终的结果。
四、动态规划的经典应用
1. 背包问题
问题描述:
- 给定
n
个物品,每个物品有重量和价值。 - 背包的最大容量是
C
,要求在不超过容量限制的情况下,使得背包中的物品总价值最大。
动态规划解法:
动态规划的核心思想是使用一个二维数组 dp[i][w]
来表示前 i
个物品放入容量为 w
的背包时的最大价值。
状态转移方程:
-
如果当前物品的重量大于背包的剩余容量,那么当前物品不能放入背包。此时最大价值等于不放当前物品时的最大价值:
d p [ i ] [ w ] = d p [ i − 1 ] [ w ] dp[i][w] = dp[i-1][w] dp[i][w]=dp[i−1][w]
-
如果当前物品的重量小于等于背包的剩余容量,我们可以选择放入或不放入当前物品:
- 不放入当前物品,最大价值为
dp[i-1][w]
。 - 放入当前物品时,最大价值为
dp[i-1][w - weight[i-1]] + value[i-1]
(即背包容量减去当前物品的重量,然后加上当前物品的价值)。
所以状态转移方程为:
d p [ i ] [ w ] = max ( d p [ i − 1 ] [ w ] , d p [ i − 1 ] [ w − w e i g h t [ i − 1 ] ] + v a l u e [ i − 1 ] ) dp[i][w] = \max(dp[i-1][w], dp[i-1][w - weight[i-1]] + value[i-1]) dp[i][w]=max(dp[i−1][w],dp[i−1][w−weight[i−1]]+value[i−1])
- 不放入当前物品,最大价值为
初始化:
dp[0][w] = 0
:表示没有物品时,不管背包容量如何,最大价值都是0。dp[i][0] = 0
:表示背包容量为0时,不管有多少物品,最大价值都是0。
时间复杂度:
- 由于
dp
数组的大小是(n+1) * (C+1)
,其中n
是物品的数量,C
是背包的容量,所以时间复杂度是O(n * C)
。
代码实现:
def knapsack(weights, values, C):"""0-1背包问题的动态规划解法:param weights: 物品的重量列表:param values: 物品的价值列表:param C: 背包的最大容量:return: 背包的最大价值"""n = len(weights) # 物品的数量# 创建一个二维dp数组,初始化为0dp = [[0] * (C + 1) for _ in range(n + 1)]# dp[i][w] 表示前i个物品在背包容量为w时的最大价值# 动态规划填充dp数组for i in range(1, n + 1): # 遍历每个物品for w in range(1, C + 1): # 遍历每个可能的背包容量if weights[i - 1] <= w:# 如果当前物品的重量小于等于当前背包容量# 选择放入当前物品或者不放入当前物品dp[i][w] = max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1])# dp[i - 1][w] 表示不放入当前物品的最大价值# dp[i - 1][w - weights[i - 1]] + values[i - 1] 表示放入当前物品的最大价值else:# 当前物品太重,不能放入背包dp[i][w] = dp[i - 1][w] # 继承上一个物品的最大价值# 返回背包容量为C时的最大价值return dp[n][C]# 示例测试
weights = [2, 3, 4, 5] # 物品的重量
values = [3, 4, 5, 6] # 物品的价值
C = 5 # 背包的最大容量print(knapsack(weights, values, C)) # 输出 7
代码解释:
- 初始化 dp 数组:
dp[i][w]
表示前i
个物品放入容量为w
的背包时的最大价值。dp
数组的大小为(n+1) * (C+1)
,初始化为0。 - 填充 dp 数组:从
i = 1
到n
,从w = 1
到C
,对于每一个物品,我们判断是否能将该物品放入当前容量为w
的背包。如果能放,则选择放与不放之间的最大值。 - 最终结果:
dp[n][C]
即为考虑所有n
个物品和容量为C
时背包的最大价值。
解释:
假设有 n = 4
个物品,分别为:
- 物品1:重量
2
,价值3
- 物品2:重量
3
,价值4
- 物品3:重量
4
,价值5
- 物品4:重量
5
,价值6
背包的容量为 C = 5
。
动态规划算法通过逐步选择物品,最终得出背包最大价值为 7
,即选择物品1(重量 2
,价值 3
)和物品2(重量 3
,价值 4
),总重量为 5
,总价值为 7
。
其他优化:
- 空间优化:可以使用一维数组优化空间,从
dp[i][w]
改为dp[w]
,每次更新时从右往左更新,避免覆盖还未使用的值。
2. 最长公共子序列(LCS)
最长公共子序列(Longest Common Subsequence, LCS)是指在给定的两个字符串中,找到一个既是这两个字符串的子序列,又是它们的最长的一个子序列。子序列的定义是:从原字符串中删除某些字符(可以不删除任何字符)而不改变其他字符的顺序,得到的新字符串就是子序列。
问题描述:
给定两个字符串 X
和 Y
,要求找出它们的最长公共子序列的长度。
动态规划解法:
用一个二维数组 dp[i][j]
来表示前 i
个字符和前 j
个字符的最长公共子序列的长度。
1. 状态定义:
dp[i][j]
表示字符串X
的前i
个字符和字符串Y
的前j
个字符的最长公共子序列的长度。
2. 状态转移方程:
-
如果
X[i-1] == Y[j-1]
:这意味着当前两个字符相同,当前最长公共子序列长度可以通过将X[i-1]
或Y[j-1]
加入到之前的最长公共子序列中得到。此时:d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] + 1 dp[i][j] = dp[i-1][j-1] + 1 dp[i][j]=dp[i−1][j−1]+1
-
如果
X[i-1] != Y[j-1]
:当前两个字符不同,意味着我们无法将这两个字符加入到公共子序列中。因此,最大值来自于之前的两种选择(要么不考虑X[i-1]
,要么不考虑Y[j-1]
):d p [ i ] [ j ] = max ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] ) dp[i][j] = \max(dp[i-1][j], dp[i][j-1]) dp[i][j]=max(dp[i−1][j],dp[i][j−1])
3. 边界条件:
dp[0][j] = 0
:当字符串X
长度为 0 时,和任何字符串Y
的最长公共子序列长度为 0。dp[i][0] = 0
:当字符串Y
长度为 0 时,和任何字符串X
的最长公共子序列长度为 0。
4. 最终结果:
最终的结果,即两个字符串的最长公共子序列的长度,将存储在 dp[len(X)][len(Y)]
中。
动态规划的空间优化:
虽然 dp
数组是二维的,但我们每次计算时仅依赖于前一行的值,因此可以通过滚动数组将空间复杂度从 O(m * n)
降低为 O(n)
。
代码实现:
def longestCommonSubsequence(X, Y):"""动态规划求解最长公共子序列(LCS)的长度:param X: 第一个字符串:param Y: 第二个字符串:return: LCS 的长度"""m = len(X) # 第一个字符串的长度n = len(Y) # 第二个字符串的长度# 初始化 dp 数组,dp[i][j] 表示 X[0..i-1] 和 Y[0..j-1] 的 LCS 长度dp = [[0] * (n + 1) for _ in range(m + 1)]# 填充 dp 数组for i in range(1, m + 1): # 遍历第一个字符串的每个字符for j in range(1, n + 1): # 遍历第二个字符串的每个字符if X[i - 1] == Y[j - 1]: # 如果当前字符相同# LCS 长度等于去掉这两个字符后的 LCS 长度加1dp[i][j] = dp[i - 1][j - 1] + 1else: # 如果当前字符不同# LCS 长度等于去掉一个字符后的 LCS 长度的最大值dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])# 返回 LCS 的长度,即 dp[m][n]return dp[m][n]# 示例测试
X = "ABCBDAB"
Y = "BDCAB"
print(longestCommonSubsequence(X, Y)) # 输出 4
代码解析:
- 初始化 dp 数组:
dp[i][j]
表示字符串X
的前i
个字符和字符串Y
的前j
个字符的最长公共子序列的长度。初始化时,dp[i][0]
和dp[0][j]
都为 0。 - 状态转移:我们通过嵌套的
for
循环遍历所有的i
和j
,根据X[i-1]
和Y[j-1]
是否相等来更新dp[i][j]
。 - 返回结果:最终的最长公共子序列的长度在
dp[m][n]
位置,即X
和Y
完整字符串的最长公共子序列长度。
示例分析:
假设 X = "ABCBDAB"
和 Y = "BDCAB"
:
- 比较
X[0]
和Y[0]
:A
和B
不同,dp[1][1] = 0
。 - 比较
X[0]
和Y[1]
:A
和D
不同,dp[1][2] = 0
。 - 比较
X[0]
和Y[2]
:A
和C
不同,dp[1][3] = 0
。 - 比较
X[0]
和Y[3]
:A
和A
相同,dp[1][4] = 1
。 - 比较
X[1]
和Y[0]
:B
和B
相同,dp[2][1] = 1
。 - 比较
X[1]
和Y[1]
:B
和D
不同,dp[2][2] = 1
。
…
最终的 dp
表格会被填充如下(仅显示关键部分):
B | D | C | A | B | ||
---|---|---|---|---|---|---|
A | 0 | 0 | 0 | 0 | 1 | 1 |
B | 0 | 1 | 1 | 1 | 1 | 2 |
C | 0 | 1 | 1 | 1 | 1 | 2 |
B | 0 | 1 | 2 | 2 | 2 | 3 |
D | 0 | 1 | 2 | 2 | 2 | 3 |
A | 0 | 1 | 2 | 2 | 3 | 3 |
B | 0 | 1 | 2 | 2 | 3 | 4 |
最终 dp[7][5] = 4
,表示字符串 X = "ABCBDAB"
和 Y = "BDCAB"
的最长公共子序列长度为 4,最长公共子序列为 "BCAB"
。
时间和空间复杂度:
- 时间复杂度:
O(m * n)
,其中m
和n
分别是两个字符串的长度,因为我们需要遍历整个dp
数组。 - 空间复杂度:
O(m * n)
,我们需要一个m * n
的二维数组来存储中间结果。
如果我们需要空间优化,可以通过滚动数组的技巧将空间复杂度降为 O(min(m, n))
。
五、动态规划的优化技巧
-
空间优化:当动态规划表格很大时,可以通过优化空间来减少空间复杂度。例如,在计算背包问题时,我们只需要保留上一行的结果,因此可以将二维数组压缩为一维数组。
-
状态压缩:将多维数组压缩成一维数组,尤其是在状态之间仅依赖于前一状态的情况。
六、总结
动态规划是解决具有最优子结构和重叠子问题的优化问题的一种强大工具。通过定义合适的状态和状态转移方程,可以将问题的复杂度从指数级降低到多项式级。掌握动态规划的技巧和方法,将能有效地解决很多经典的计算机算法问题,如背包问题、最长公共子序列、编辑距离等。
相关文章:

详解动态规划算法
动态规划 一、动态规划的核心思想二、动态规划的步骤1. 定义状态(State)2. 确定状态转移方程(State Transition Equation)3. 确定边界条件(Base Case)4. 填表(Table Filling)或递归计…...

LINUX网络基础 [五] - HTTP协议
目录 HTTP协议 预备知识 认识 URL 认识 urlencode 和 urldecode HTTP协议格式 HTTP请求协议格式 HTTP响应协议格式 HTTP的方法 HTTP的状态码 编辑HTTP常见Header HTTP实现代码 HttpServer.hpp HttpServer.cpp Socket.hpp log.hpp Makefile Web根目录 H…...

慕慕手记项目日志 项目从开发到部署多环境配置 2025-3-8
慕慕手记项目日志 项目从开发到部署多环境配置 2025-3-8 现在是已经到了课程的第十章了,开始进行配置项目环境了。现在要完成的任务是项目可以正常运行,而且可以自由切换配置,开发/测试。 下面是当前的目录结构图: 现在来解释一…...

华为配置篇-OSPF基础实验
OSPF 一、简述二、常用命令总结三、实验3.1 OSPF单区域3.2 OSPF多区域3.3 OSPF 的邻接关系和 LSA 置底 一、简述 OSPF(开放式最短路径优先协议) 基本定义 全称:Open Shortest Path First 类型:链路状态路由协议(IGP&…...

闭包:JavaScript 中的隐形大杀器
你可能已经在很多地方听说过闭包这个词,尤其是涉及到 JavaScript 的作用域和异步操作时。闭包是 JavaScript 中非常核心的概念,然而它又非常容易让开发者感到困惑。今天我们就来深入剖析闭包,帮助你真正理解它的工作原理,以及如何…...

【消息队列】数据库的数据管理
1. 数据库的选择 对于当前实现消息队列这样的一个中间件来说,具体要使用哪个数据库,是需要稍作考虑的,如果直接使用 MySQL 数据库也是能实现正常的功能,但是 MySQL 也是一个客户端服务器程序,也就意味着如果想在其他服…...

玩转ChatGPT:GPT 深入研究功能
一、写在前面 民间总结: 理科看Claude 3.7 Sonnet 文科看DeepSeek-R1 那么,ChatGPT呢? 看Deep Research(深入研究)功能。 对于科研狗来说,在这个文章爆炸的时代,如何利用AI准确、高效地收…...

Centos8部署mongodb报错记录
使用mongo ops安装mongodb6.0.4副本集报错 error while loading shared libraries: libnetsnmpmibs.so.35: cannot open shared object file: No such file or directory 解决 yum install net-snmp net-snmp-devel -y建议:初始化系统时把官网上的依赖包都装一遍 即…...

2024四川大学计算机考研复试上机真题
2024四川大学计算机考研复试上机真题 2024四川大学计算机考研复试机试真题 历年四川大学计算机考研复试机试真题 在线评测:https://app2098.acapp.acwing.com.cn/ 分数求和 题目描述 有一分数序列: 2/1 3/2 5/3 8/5 13/8 21/13… 求出这个数列的前 …...

前端面试题 口语化复述解答(从2025.3.8 开始频繁更新中)
背景 看了很多面试题及其答案。但是过于标准化,一般不能直接用于回复面试官,这里我将总结一系列面试题,用于自我复习也乐于分享给大家,欢迎大家提供建议,我必不断完善之。 Javascript ES6 1. var let const 的区别…...

更多文章请查看
更多文章知识请移步至下面链接,期待你的关注 如需查看新文章,请前往: 博主知识库https://www.yuque.com/xinzaigeek...

vulnhub靶场之【digitalworld.local系列】的vengeance靶机
前言 靶机:digitalworld.local-vengeance,IP地址为192.168.10.10 攻击:kali,IP地址为192.168.10.6 kali采用VMware虚拟机,靶机选择使用VMware打开文件,都选择桥接网络 这里官方给的有两种方式ÿ…...

MySql的安装及数据库的基本操作命令
1.MySQL的安装 1.1进入MySQL官方网站 1.2点击下载 1.3下拉选择MySQL社区版 1.4选择你需要下载的版本及其安装的系统和下载方式 直接安装以及压缩包 建议选择8.4.4LST LST表明此版本为长期支持版 新手建议选择红框勾选的安装方式 1.5 安装包下载完毕之后点击安装 2.数据库…...

中性点直接接地电网接地故障Simulink仿真
1.模型简介 本仿真模型基于MATLAB/Simulink(版本MATLAB 2017Ra)软件。建议采用matlab2017 Ra及以上版本打开。(若需要其他版本可联系代为转换) 2.系统仿真图: 3.中性点直接接地电网接地故障基本概念(本仿…...

Linux16-数据库、HTML
数据库: 数据存储: 变量、数组、链表-------------》内存 :程序运行结束、掉电数据丢失 文件 : 外存:程序运行结束、掉电数据不丢失 数据库: …...

SpireCV荣获Gitee 最有价值开源项目称号
什么是GVP? GVP全称Gitee Valuable Project,意思为Gitee最有价值开源项目。作为GVP称号的获得者,SpireCV在开源社区中展现出了卓越的实力和影响力,为开源软件的发展和推广做出了积极的贡献。 这一荣誉不仅充分肯定了过去阿木实验…...

open-webui+deepseek api实现deepseek自由
虽然deepseek是免费的,但是官网的体验感太差。 所以才会有某种想法,想要更加舒服的使用deepseek。 1.Python虚拟环境 这个我就不多赘述,切记Python版本一定要和open-webui制定的版本一致。 2.deepseek api 去这个网站充点钱(…...

Hadoop八股
Hadoop八股 HadoopMapReduce考点MR on Yarn 分布式工作原理shuffle:MapTask 和 ReduceTask的处理流程MR中的MapTask 和 ReduceTask 的数量决定MR和Spark两者Shuffle的区别简单讲一下map- reduce 原理**MapReduce 的核心概念****MapReduce 的工作流程****MapReduce 的…...

.NET Core全屏截图,C#全屏截图
.NET Core全屏截图,C#全屏截图 使用框架: WPF.NET 8 using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Text; using System.Threading.Tasks; using System.W…...

ajax之生成一个ajax的demo示例
目录 一. node.js和express 二. 使用express创建后端服务 三. 创建前端 一. node.js和express ajax是前端在不刷新的情况下访问后端的技术,所以首先需要配置一个后端服务,可以使用node.js和express。 首先生成一个空项目,新建main目录…...

软件工程笔记下
从程序到软件☆ 章节 知识点 概论☆ 软件的定义,特点,生存周期。软件工程的概论。软件危机。 1.☆软件:软件程序数据文档 (1)软件:是指在计算机系统的支持下,能够完成特定功能与性能的包括…...

【自学笔记】Numpy基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Numpy基础知识点总览目录1. 简介Numpy是什么为什么使用Numpy 2. 数组对象(ndarray)创建数组数组的属性数组的形状操作 3. 数组的基本操作数组…...

大模型gpt结合drawio绘制流程图
draw下载地址 根据不同操作系统选择不同的安装 截图给gpt 并让他生成drawio格式的,选上推理 在本地将生成的内容保存为xml格式 使用drawio打开 保存的xml文件 只能说效果一般。...

3.8【Q】cv
这个draw_line函数的逻辑和功能是什么?代码思路是什么?怎么写的? 这个t是什么?t.v[0]和t.v[1],[2]又是什么? void rst::rasterizer::draw(rst::pos_buf_id pos_buffer, rst::ind_buf_id ind_buffer, rst::Primitive ty…...

STM32F10XXX标准库函数及外设结构体
时钟 APB1 void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState):使能或失能 APB1 时钟 参数 可赋值 描述 RCC_APB1Periph RCC_APB1Periph_TIM2 RCC_APB1Periph_TIM3 RCC_APB1Periph_TIM4 RCC_APB1Periph_TIM5 RCC_APB1Peri…...

计算机毕业设计SpringBoot+Vue.js车辆管理系统(源码+文档+PPT+讲解)
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

剖析Manus:AI领域的创新先锋还是虚假泡沫?
在AI技术迅猛发展的当下,新的智能体不断涌现,其中Manus的出现可谓是一石激起千层浪。近期,OpenManus以极快速度复刻Manus,引发了广泛关注,但这也让我们更有必要深入剖析Manus,探究它究竟是货真价实的创新突…...

编程考古-Borland历史:《.EXE Interview》对Anders Hejlsberg关于Delphi的采访内容(中)
为了纪念Delphi在2002年2月14日发布的25周年(2020.2.12),这里有一段由.EXE杂志编辑Will Watts于1995年对Delphi首席架构师Anders Hejlsberg进行的采访记录。在这次采访中,Anders讨论了Delphi的设计与发展,以及即将到来的针对Windows 95的32位版本。 Q. 编译器引擎本身是用…...

GB28181视频平台LiveGBS在设置公网IP收流时,如何自定义修改收流端口区间
LiveGBS GB28181流媒体服务在接收视频的时候默认是使用30000-30249, webrtc流播放端口区间默认是UDP的30250-30500区间。有些网络环境不方便开放这么大的端口区间,下面介绍下如何修改配置这个区间。 从页面上修改这个区间,端口区间尽量设置大…...

【ubuntu20】--- 搭建 gerrit 最新最详细
在编程的艺术世界里,代码和灵感需要寻找到最佳的交融点,才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里,我们将共同追寻这种完美结合,为未来的世界留下属于我们的独特印记。 【ubuntu20】--- 搭建 gerrit 最新最详细…...