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

动态规划入门

前言:首先,什么是动态规划?

      动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中,可能会有许多可行解。每一个解都对应于一个值,我们希望找到具有最优值的解。动态规划算法与分治法类似,其基本思想也是将待求解问题分解成若干个子问题,先求解子问题,然后从这些子问题的解得到原问题的解。与分治法不同的是,适合于用动态规划求解的问题,经分解得到子问题往往不是互相独立的。

      我们都熟知的斐波那切数列问题就是典型的动态规划问题,后面的问题的解,与前面已经求出来的问题的解有关,也就是我们经常在递归中用公式   f[i]=f[i-1]+f[i-2](i>2),在动态规划中,我们也把这种类型的公式叫做状态转移方程。

      我们的动态规划算法,本质上是由dfs而来,经历了dfs->记忆化搜索->动态规划的过程,可以说,动态规划就是dfs的提高。

1.典例引入:

爬楼梯(斐波那切数列的应用)

 这就是典型的斐波那切数列问题,我们跳到第i级台阶有两种可行方案,第一种是从第i-1级台阶上跳一步,另一种是从第i-2级台阶跳两步,而第i-1级台阶和第i-2级台阶也分别可以由两种不同的方案,当然我们需要知道前两个初始值,以便让我们开始循环求解方案数,而我们易知第一级台阶只有一种方案,第二节台阶有两种方案,从第三极台阶开始我们就可以通过递归来求解了,当然,这个例子想必自然不用多说,但凡学过递归的,肯定都是老生常谈的例子了,这里直接给出代码,给后续的例题提供模板。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 20;
int n;
int dfs(int n)
{if (n == 1)return 1;else if (n == 2)return 2;elsereturn  dfs(n - 1) + dfs(n - 2);
}
int main()
{cin >> n;cout << dfs(n) << endl;return 0;
}

1.1从dfs到记忆化搜索再到动态规划的优化算法

上面的斐波那切数列数列式的问题,在数据不大的前提下我们的dfs是可行的,但是,我们知道dfs的执行过程实际上是极为复杂的,这中间多次重复的调用已经求出的值,我们可以进一步优化算法,把子问题的答案存起来,去掉重复值;

1.1.1 记忆化搜索

所谓记忆化搜索,就是将已经求出的子问题的答案保存在数组中,当我们在求解一个问题是,先查找数组中是否已有答案,查到直接返回数组值,没查到再进行递归求解,这样可以省去重复的分支,优化时间复杂度。(这里不考虑数据溢出的问题,事实上,当我们的n为50的时候就已经爆int了~~

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int mem[maxn];
int dfs(int n)
{if (mem[n])return mem[n];int sum = 0;if (n == 1)sum = 1;else if (n == 2)sum = 2;elsesum = dfs(n - 1) + dfs(n - 2);mem[n] = sum;return sum;
}
int main()
{cin >> n;memset(mem, 0, sizeof(mem));cout << dfs(n) << endl;return 0;
}

1.1.2 动态规划

我们的动态规划实际上就是记忆化搜索的改进,本质上还是递归,递(把一个大问题分解成一个个子问题,相当于自顶向下求解)归(由最小子问题答案带回求出大问题答案,相当于自底向上返回大问题答案),动态规划,就是省去“递”的过程,直接从已知子问题开始,逐步求解大问题,直到求出大问题的解。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int f[maxn];//sum = dfs(n - 1) + dfs(n - 2);int main()
{cin >> n;f[1] = 1;f[2] = 2;if(n==1||n==2){cout<<n<<endl;return 0;}for (int i = 3; i <= n; i++)f[i] = f[i - 1] + f[i - 2];cout << f[n] << endl;return 0;
}

*1.1.3三变量的再优化(空间复杂度的优化)

在上述的递推过程中我们不难发现,f[i]的取值只和f[i-1]还有f][i-2]有关,我们可以直接用三个变量来代替整个数组来优化空间,这个并不难能想到,这里只是一提,事实上,在一些竞赛等方面,时间复杂的才是更重要的考察点。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n;
int ans = 0;
int main()
{cin >> n;if (n == 1 || n == 2){cout << n << endl;return 0;}int f1 = 1, f2 = 2;for (int i = 3; i <= n; i++){ans = f1 + f2;f1 = f2;f2 = ans;}cout << ans << endl;return 0;
}

2.渐入佳境

相信你可能已经跃跃欲试了,这不就来了嘛

 

 这个题可能刚开始并不容易想到思路,因为这并不像前面的爬楼梯一样是简单的方案数相加问题了,这是一道求最优解的问题,我们来捋下思路,

那我,我们现在就可以很容易的写出dfs版的代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int ans = 0;
int w[maxn];//表示店铺的价值
int dfs(int x)//x表示第x家店铺
{if (x > n)return 0;elsereturn max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺
}
int main()
{cin >> T;while (T--){cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];int ret=dfs(1);cout << ret << endl;}return 0;
}

上面的代码很存在很多的重复性的子分支存在,很遗憾的,这段代码会TLE的,那我在我们之前的优化中,就要采用记忆化搜索和动态规划来优化时间了,

2.1.记忆化搜索改进

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int ans = 0;
int w[maxn];//表示店铺的价值
int mem[maxn];
int dfs(int x)//x表示第x家店铺
{if (mem[x])return mem[x];int sum = 0;if (x > n)sum = 0;elsesum= max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺mem[x] = sum;return sum;
}
int main()
{cin >> T;while (T--){memset(mem, 0, sizeof(mem));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];int ret=dfs(1);cout << ret << endl;}return 0;
}

2.2动态递归

我们在一般的竞赛中,记忆化数组还是不常用的,事实上,上一个题的记忆化搜索已经可以AC了,我们还是来搞一下比较常用的动态规划

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int w[maxn];//表示店铺的价值
int f[maxn];//max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺int main()
{cin >> T;while (T--){memset(f, 0, sizeof(f));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];for (int i = n; i >= 1; i--){f[i] = max(f[i+1], f[i + 2] + w[i]);//由底层求到根节点}cout << f[1] << endl;}return 0;
}

很多人可能不理解为什么要逆序来进行递推,说实话,一开始我也不太理解,但是我们来看,在上面的二叉树类型的结构中,我们需要根据上面所求得的答案来推出下面的最优解,也就是相当于最小的已知子问题是我先把第一个元素作为条件开始进行递归的,第一个子问题的答案我们是知道的,动态规划的本质就是从大的未知问题向小的已知的子问题的“归”操作,并且我们的递归过程一定是从大问题到小问题的“回溯”过程中才能得出答案的,假如我们用的是正序,那么状态转移方程就要变变形式了。

#include <bits/stdc++.h>using namespace std;
const int maxn = 1e5 + 5;
int T,n;
int w[maxn];//表示店铺的价值
int f[maxn];//max(dfs(x + 1), dfs(x+2) + w[x]);//x+1表示第x家店铺不选,那么这家店铺的价值就肯定得不到了,x+2表示选择第x+2加店铺,说明选择了第x家店铺int main()
{cin >> T;while (T--){memset(f, 0, sizeof(f));cin >> n;for (int i = 1; i <= n; i++)cin >> w[i];//for (int i = n; i >= 1; i--)//{//	f[i] = max(f[i+1], f[i + 2] + w[i]);//由底层求到根节点//}//cout << f[1] << endl;for (int i = 1; i <= n; i++){//f[i] = max(f[i - 1], f[i - 2] + w[i]);//因为i为1时数组会越界,所以我们同时让下标加2f[i+2] = max(f[i+1], f[i] + w[i]);//由根节点求到叶子结点}cout << f[n+2] << endl;}return 0;
}

可见两种形式的状态转移方程虽然不相同,但是两者的本质都是通过大问题像小问题递归进行的求解。

3.典例1

 

 题目并不难理解(看好了哈,这个不是那个有向左走次数与向右走次数的差不超过1的那个),他是想让我们找出来一条最长路径,结合我们的前面的讲解,我们不难给出下面的dfs代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int n;
int mp[maxn][maxn];//表示店铺的价值int dfs(int x, int y)
{if (x > n||y>n)return 0;elsereturn max(dfs(x+1,y),dfs(x+1,y+1)) + mp[x][y];
}
int main()
{cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> mp[i][j];cout << dfs(1, 1) << endl;return 0;
}

看看数据量,1000行,dfs肯定会崩掉的,我们只能来优化,

记忆化搜索的方法我这里就不展开写了,毕竟模板在那挺好写的,我们直接来看重头戏动态规划

3.1动态规划法降低时间复杂度

我们先来画出递归树:

从以上来看,我们的递推方程也会有正推和逆推两种方式,这两种方式的不同之处在于:

对于正推(也就是从上往下推)

      我们现在已知的子问题是最上层的7这个点,因为它只有一个选择,我们需要每次为该层的子问题选择最优解,我们知道从上往下看,要求是选右下或者正下(在二维数组中是这样的,在该图中就是左下),那么相当于我们在二维数组中,我们向最大层靠近(向下层)的过程中,就要每次选择上层的正上方的数或者左上方的数,然后取他们的最大值即可,当我们取到最后一行时,因为我们是for循环遍历,所以每个数都会遍历到,也就造成了我们的第n行每一列都会有一个最大值,我们无法确定最后一行哪一列才是最大值,所以最后还需要求出最大的那个数

对于逆推

     逆推则是从第n行出发,我们每次寻找的是使我们当前这一层能取到最,我们的最终结果应该汇聚到最顶部的根节点上,所以我们应该把每层的结果放到上一层中去,逐层往上送至顶点处,所以我们对于每个点的选择应该还是遵从选择右下或者正下的,也就是说,我们应该使起点处为该节点所对应的值(比如最后一行的第一个节点对应的值应该为4),所以虽然是向上遍历,但是我们的选择还是来自下方的两个数,这样既保证了我们第n行的n个起点的值为节点值,也保证了我们选择最大值得目的。

这是代码:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int n;
int mp[maxn][maxn];//表示店铺的价值
int f[maxn][maxn];int main()
{cin >> n;for (int i = 1; i <= n; i++)for (int j = 1; j <= i; j++)cin >> mp[i][j];//正推for(int i=1;i<=n;i++)for (int j = 1; j <= i; j++){f[i][j] = max(f[i -1][j], f[i - 1][j - 1]) + mp[i][j];}//这里我们需要注意的是,我们一次所求出的f[i][j]只是相对的第n行其中一个位置的加和最大值,也就是说我们在这次循环结束后并不能马上求出最大值,我们的最大值在第n行中,但是我们不能确定在第几列,所以我们要排序寻找sort(f[n]+1, f[n] + n+1);cout << f[n] << endl;//逆推//for (int i = n; i >= 1; i--)//	for (int j = 1; j <= i; j++)//		f[i][j] = max(f[i + 1][j], f[i + 1][j + 1]) + mp[i][j];我们的终点只有一个,所以说递推的终点只有一个//cout << f[1][1] << endl;return 0;
}

间章规律小总结(不知对否,还请慎读)

       从上面的例子中我们不难的发现,动态规划本质上就是递归加递推的过程,一边递归一遍利用递推公式求解上层问题,其中递归的过程体现在for循环中,递推的过程体现在状态转移方程中,还记得我开头提过的动态规划在“归”的过程中出结果吗,准确的来说是在递推的过程中出结果,我们都知道“递”和“归”是两个相反的过程,同样的,递归和递推也是如此,细心地朋友可能会发现,在上面的两种正逆序的不同遍历过程中,实际上他们所对应的递推公式也是相反的,直白的说,for循环和状态转移方程是相反的,在我们上面的典例1中,for循环由上向下遍历,那么状态转移方程的每一个状态就和前一个状态有关,反之,如果是逆序遍历,相当于递归方向由下到上,那么递推就要与其相反才能有效的求出结果从而退出递归状态,因而每个点的状态由他的后一个状态求出,(如果我嘟囔错了,还请给位大佬帮忙指正~~多谢)。

By the  way:前面的f表示的二维数组其实可以直接优化成一维数组,我们只需要保留上一层的对应的状态即可(别试正推,因为不行~QAQ)

 但是这个要慎用,一般不推荐,搞不好正逆推会出错的,写这个只是为了知道一些大佬写题解会用一维数组就行了。

 

4.最后一站:让我们来到经典背包问题

输入样例:

4 5
1 2
2 4
3 4
4 5

 输出样例:

8

这个问题可能和前面的大盗阿福类似,还是在限制条件下求解最优解问题,我们老规矩,先来写经典的dfs版本:

#include <bits/stdc++.h>using namespace std;
const int maxn = 1005;
int N,V;
int w[maxn], v[maxn];
int dfs(int x, int surV)//surV表示当前背包的剩余体积,x表示当前正在考虑第x个物品
{if (x > N)return 0;if (v[x] > surV)//该物品超出当前背包剩余体积,装不下,被迫继续寻找下一个物品return dfs(x + 1, surV);else if(surV>=v[x]){//能装下,但是我们可以自由选择装与不装return max(dfs(x + 1, surV - v[x])+w[x], dfs(x + 1, surV));}
}
int main()
{cin >> N >> V;for (int i = 1; i <= N; i++)cin >> v[i] >> w[i];cout << dfs(1, V) << endl;//从第一个物品开始考虑return 0;
}

下面还是直接看动态规划版本:记忆化搜索就是个模板,不是太难理解,而且和dfs差距不大。

建议还是先理解前面正逆序方式的不同:

#include <bits/stdc++.h>using namespace std;
const int maxn = 105;
int N,V;
int dp[maxn][maxn];//第一个下标表示背包的当前选择的物品,第二个下标为当前背包剩余体积
int w[maxn], v[maxn];
int dfs(int x, int surV)
{if (x > N)return 0;if (v[x] > surV)//该物品超出当前背包剩余体积,装不下,被迫继续寻找下一个物品return dfs(x + 1, surV);else if(surV>=v[x]){//能装下,但是我们可以自由选择装与不装return max(dfs(x + 1, surV - v[x])+w[x], dfs(x + 1, surV));}
}
int main()
{cin >> N >> V;for (int i = 1; i <= N; i++)cin >> v[i] >> w[i];倒推//for (int i = N; i >= 1; i--)//{//	for (int j = 0; j <= V; j++)//	{//		if (j < v[i])//			dp[i][j] = dp[i + 1][j];//		else if(j>=v[i])//		    dp[i][j] = max(dp[i + 1][j], dp[i + 1][j - v[i]] + w[i]);//看到没,和我说的规律一样//	}//}// cout<<dp[1][V]<<endl;//终点在第一个物品//正推for(int i=1;i<=N;i++)for (int j = 0; j <= V; j++){if (j < v[i])dp[i][j] = dp[i - 1][j];else if (j >= v[i])dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + w[i]);//看到没,和我说的规律一样}cout << dp[N][V] << endl;//终点在第N个物品return 0;
}

5.总结

       动态规划是一个上下限都极高的算法,想要学会并不容易,不知我这七千五百字能够让你们学到多少,动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间。

6.金句省身

       不要期待突如其来的惊喜,更不要期待命运会突然得到改变。没有一蹴而就的成功,好运不过是努力的伏笔而已。真正让我们成长的力量,就藏在那些日复一日看似平常的坚持里。所以,不要抱怨,你只管努力,该来的成功不会远。

                                                                                                          ----------   摘自《人民日报》

 

相关文章:

动态规划入门

前言&#xff1a;首先&#xff0c;什么是动态规划&#xff1f; 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。动态规划算法与分治法类似&#xff0c;其基本思…...

day26 SpringBootWeb案例(二)

目录 SpringBootWeb案例 1. 新增员工 1.1 需求 1.2 开发 1.3 测试 2. 文件上传 2.1 简介 2.2 本地存储 2.3 阿里云OSS 3. 配置文件 3.1 Value 3.2 yml配置 3.3 ConfigurationProperties 4. 修改员工 4.1 需求 4.2 查询回显 4.3 保存修改 SpringBootWeb案例 前…...

力扣-《剑指offer》-哈希表-刷题笔记

目录 第一题&#xff1a;03.数组中重复的数字 第二题&#xff1a;39.数组中出现次数超过一半的数字 第三题&#xff1a;50.第一个只出现一次的字符 第四题&#xff1a;53. &#xff08;0-n-1&#xff09;中缺失的数字 第一题&#xff1a;03.数组中重复的数字 我的答案&…...

【SpringBoot】| 邮箱发送验证码,你会了吗?

目录&#x1f981; 题外话&#x1f981; 提前准备2.1 配置邮箱第三方登录2.1.1 点击设置——账户2.1.2 开启POP3/SMTP服务2.2 添加依赖2.3 yaml配置&#x1f981; 进入主题&#x1f981; 测试使用&#x1f981; 尾声3.1 安利一个生成验证码的工具类3.1.1 添加依赖3.1.2 编写配置…...

Linux系统安装部署及配置Grafana

TOC 用于 UI 展示 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpm1 安装 grafana 1.1 下载安装 wget https://dl.grafana.com/oss/release/grafana-8.0.3-1.x86_64.rpmsudo yum install grafana-8.0.3-1.x86_64.rpm1.2 启动&状态查看 sudo syst…...

Python3 入门教程||Python3 输入和输出||Python3 File 方法

Python3 输入和输出 在前面几个章节中&#xff0c;我们其实已经接触了 Python 的输入输出的功能。本章节我们将具体介绍 Python 的输入输出。 输出格式美化 Python 两种输出值的方式: 表达式语句和 print() 函数。(第三种方式是使用文件对象的 write() 方法; 标准输出文件可以…...

有效的字母异位词(力扣刷题)

给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 示例 1: 输入: s "anagram", t "nagaram" 输出: true 示例 2: 输入: s "rat", t "car" 输出: false 说明: 你可以假设字符串只包含小写字母。 …...

73、介绍下 HashMap 的底层数据结构

73、介绍下 HashMap 的底层数据结构 我们现在用的都是 JDK 1.8&#xff0c;底层是由“数组链表红黑树”组成&#xff0c;如下图&#xff0c;而在 JDK 1.8 之前是由“数组链表”组成。 1.Hash Hash叫做”散列表“&#xff0c;就是把任意长度的输入&#xff0c;通过散列算法&am…...

系统集成路由器OSPF动态、综合路由配置

实验任务&#xff1a;动态路由协议RIP、OSPF协议的内容和特点动态路由RIP、OSPF实验&#xff0c;建立拓扑pc1>>R1>>R2>>R3>>pc2&#xff0c;使pc1与pc2能相互通信&#xff0c;并配置PC端静默接口。熟悉配置vlan间路由技术&#xff1a;多层交换机虚拟接…...

【力扣周赛 338】

6354. K 件物品的最大和 - 力扣&#xff08;Leetcode&#xff09;袋子中装有一些物品&#xff0c;每个物品上都标记着数字 1、0或 -1。给你四个非负整数 numOnes、numZeros、numNegOnes和 k。袋子最初包含&#xff1a;numOnes 件标记为 1 的物品。numZeroes 件标记为 0 的物品。…...

大数据Flink进阶(八):Apache Flink架构介绍

Apache Flink架构介绍 一、Flink组件栈 在Flink的整个软件架构体系中,同样遵循这分层的架构设计理念,在降低系统耦合度的同时,也为上层用户构建Flink应用提供了丰富且友好的接口。...

Mars3d项目启动上的一些坑

前言 最近新入职了一家公司&#xff0c;公司新开了有个未来城市的项目&#xff0c;需要用到3D城市建模&#xff0c;公司老总选了Mars3d作为前端框架&#xff0c;项目分给我了&#xff0c;又是一个全新的领域&#xff0c;开搞吧&#xff01; 下面是自己遇到的几个小问题&#x…...

通俗易懂【Springboot】 单文件下载和批量下载(多个文件合成一个压缩包下载)

文章目录一.单文件下载1.简单理解文件下载2.单文件下载的具体代码实现3.测试4.单文件下载整体代码二.多文件批量下载&#xff08;多个文件合成一个压缩包下载&#xff09;1.多文件下载的实现方式&#xff0c;这里使用了ZipOutputStream2.具体代码实现3.测试4.文件批量下载&…...

CnOpenData中国行政区划shp数据

一、数据简介 中国行政区划数据是重要的基础地理信息数据&#xff0c;目前不同来源的全国行政区划数据非常多&#xff0c;但能够开放获取的高质量行政区域数据少之又少。基于此&#xff0c;锐多宝的地理空间制作一套2013-2023年可开放获取的高质量行政区划数据。该套数据以2022…...

GPT-4零失误通关大厂模拟面试,offer拿到手软?与AGI首次接触

来源: FoxyearMeta “GPT-4可被视作AGI &#xff08;通用人工智能&#xff09;的早期版本。” 如若从他人口中说出&#xff0c;或许是无稽之谈—— 但是由微软雷蒙德研究院机器学习理论组负责人万引大神Sbastien Bubeck与2023新视野数学奖得主Ronen Eldan、2023新晋斯隆研究奖得…...

Hardhat 环境搭建及教程示例

一.安装node.js curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash nvm install 18 nvm use 18 nvm alias default 18 npm install npm --global # Upgrade npm to the latest version 二. 安装hardhat 2.1 创建hardhat安装目录 mkdir hard…...

复杂链表的复制-剑指Offer35-java

一、题目描述 请实现 copyRandomList 函数&#xff0c;复制一个复杂链表。在复杂链表中&#xff0c;每个节点除了有一个 next 指针指向下一个节点&#xff0c;还有一个 random 指针指向链表中的任意节点或者 null。 示例 1&#xff1a; 输入&#xff1a;head [[7,null],[13,…...

【Linux】进程理解与学习Ⅰ-进程概念

环境&#xff1a;centos7.6&#xff0c;腾讯云服务器Linux文章都放在了专栏&#xff1a;【Linux】欢迎支持订阅&#x1f339;相关文章推荐&#xff1a;【Linux】冯.诺依曼体系结构与操作系统进程概念什么是进程&#xff1f;进程是什么&#xff1f;我们打开任务管理器可以看到有…...

WebKitX ActiveX 6.0 X86 Crack

WebKitX ActiveX将 Chromium Embedded Framework (CEF3) 包装到一个进程外的 ActiveX 组件中&#xff0c;以便与 OLE/COM 语言一起使用。Chromium Embedded Framework 封装了 WebKit Blink HTML5 Renderer 和 Google V8 JavaScript Engine。这是一个用于商业用途的生产级稳定组…...

开源项目:数据库表结构生成文档工具

目录 一、软件介绍 二、技术框架 三、功能介绍 四、代码展示 1、获取数据库信息部分代码 2、导出Html文档代码 五、运行效果 六、项目开源地址 一、软件介绍 今天给大家分享我自己编写的数据库表结构文档生成工具&#xff0c;方便大家在实际开发当中&#xff0c;可以很方便导出…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...