【图论】(四)最小生成树与拓扑排序

最小生成树与拓扑排序

  • 最小生成树之prim(P算法)
    • 相关概念
    • 结题思路
    • 拓展
  • 最小生成树之kruska(K算法)
    • 过程模拟
    • 程序实现
    • 拓展
  • 拓扑排序
    • 背景与思路
    • 模拟过程
    • 程序实现

最小生成树之prim(P算法)

相关概念

P算法是用于求最小生成树的算法。

  • 最小生成树是所有节点的最小连通子图, 即:以最小的成本(边的权值)将图中所有节点链接到一起

  • 图中有 n 个节点,那么一定可以用 n - 1 条边将所有节点连接到一起。

  • 那么在这个图中,如何选取 n-1 条边 使得 图中所有节点连接到一起,并且边的权值和最小呢?

输入描述: 第一行包含两个整数V 和 E,V代表顶点数,E代表边数 。顶点编号是从1到V。例如:V=2,一个有两个顶点,分别是1和2。

接下来共有 E 行,每行三个整数 v1,v2 和 val,v1 和 v2 为边的起点和终点,val代表边的权值。

输出描述: 输出联通所有节点的最小路径总距离

输入示例:

7 11
1 2 1
1 3 1
1 5 2
2 6 1
2 4 2
2 3 2
3 4 1
4 5 1
5 6 2
5 7 1
6 7 1

输出示例:

6

prim算法 是从节点的角度 采用贪心策略 每次寻找距离 最小生成树最近的节点 并加入到最小生成树中。

prim算法核心就是三步,称为prim三部曲,非常重要

prim三部曲,非常重要

prim三部曲,非常重要

在prim算法中,有一个数组特别重要,这里我起名为:minDist。

“每次寻找距离 最小生成树最近的节点 并加入到最小生成树中”,那么如何寻找距离最小生成树最近的节点呢?

minDist数组 用来记录 每一个节点距离最小生成树的最近距离

为了方便数组下标和节点对应,minDist数组下标从 1 开始计数,下标0 就不使用了

结题思路

第一步:初始状态

  • 还没有最小生成树,默认每个节点距离最小生成树是最大的,这样后面我们在比较的时候,发现更近的距离,才能更新到 minDist 数组上。
  • minDist 数组 里的数值初始化为 最大数,因为本题 节点距离不会超过 10000,所以 初始化最大数为 10001。

如图:

第二步

  • 第一步:选距离生成树最近节点: 选择距离最小生成树最近的节点,加入到最小生成树,刚开始还没有最小生成树,所以随便选一个节点加入就好,为了方便选择1

  • 第二步:最近节点加入生成树: 此时 节点1 已经算最小生成树的节点。

  • 第三步:更新非生成树节点到生成树的距离(即更新minDist数组): 更新所有节点距离最小生成树的距离,如图:


此时所有非生成树的节点距离 最小生成树(节点1)的距离都已经跟新了 。

  • 节点2 与 节点1 的距离为1,比原先的 距离值10001小,所以更新minDist[2]。
  • 节点3 和 节点1 的距离为1,比原先的 距离值10001小,所以更新minDist[3]。
  • 节点5 和 节点1 的距离为2,比原先的 距离值10001小,所以更新minDist[5]。

第三步

  • 第一步:选距离生成树最近节点: 选取一个距离 最小生成树(节点1) 最近的非生成树里的节点,节点2,3,5 距离 最小生成树(节点1) 最近,选节点 2(其实选 节点3或者节点2都可以,距离一样的)加入最小生成树。
  • 第二步:最近节点加入生成树: 节点1 和 节点2,已经算最小生成树的节点。
  • 第三步:更新非生成树节点到生成树的距离(即更新minDist数组): 接下来,我们要更新节点距离最小生成树的距离,如图


此时所有非生成树的节点距离 最小生成树(节点1、节点2)的距离都已经跟新了 。

  • 节点3 和 节点2 的距离为2,和原先的距离值1 小,所以不用更新。
  • 节点4 和 节点2 的距离为2,比原先的距离值10001小,所以更新minDist[4]。
  • 节点5 和 节点2 的距离为10001(不连接),所以不用更新。
  • 节点6 和 节点2 的距离为1,比原先的距离值10001小,所以更新minDist[6]。

第四步

  • 第一步:选距离生成树最近节点: 选择一个距离 最小生成树(节点1、节点2) 最近的非生成树里的节点,节点3,6 距离 最小生成树(节点1、节点2) 最近,选节点3 (选节点6也可以,距离一样)加入最小生成树

  • 第二步:最近节点加入生成树: 此时 节点1 、节点2 、节点3 算是最小生成树的节点。

  • 第三步:更新非生成树节点到生成树的距离(即更新minDist数组): 接下来更新节点距离最小生成树的距离,如图:


所有非生成树的节点距离 最小生成树(节点1、节点2、节点3 )的距离都已经跟新了 。

  • 节点 4 和 节点 3的距离为 1,和原先的距离值 2 小,所以更新minDist[4]为1。

上面为什么我们只比较 节点4 和 节点3 的距离呢?

因为节点3加入 最小生成树后,非 生成树节点 只有 节点 4 和 节点3是链接的,所以需要重新更新一下 节点4距离最小生成树的距离,其他节点距离最小生成树的距离 都不变。

程序实现

图的存储: 这里采用邻接矩阵进行对图的存储

    int v, e;int x, y, k;cin >> v >> e;// 填一个默认最大值,题目描述val最大为10000vector<vector<int>> grid(v + 1, vector<int>(v + 1, 10001));while (e--) {cin >> x >> y >> k;// 因为是双向图,所以两个方向都要填上grid[x][y] = k;grid[y][x] = k;}

步骤一:选距离生成树最近节点: 每次遍历节点,查找每个节点到生成树的距离,获取到生成树最近节点的编号与距离,因此这里有几个关键的几个变量:

  • minDist[]:标记不在生成树中的节点到生成树的距离
  • isInTree:标记该节点是否在生成树中
  • cur:记录每一轮到生成树最小节点的标号
  • minDis:记录每一轮节点到生成树的最小距离

加入生成树节点的条件:

  • 本来就不在生成树中
  • 到生成树的距离 minDist[j] 最小
// 标记节点是否在生成树中
vector<bool> isInTree(v+1, false);
// 记录非生成树中的节点到生成树的最短距离
vector<int> minDist(v+1, 10001);// 步骤一、选距离生成树最近节点
int cur = -1;			// 待加入到生成树中的节点
int minDis = INT_MAX;	// 记录本轮离生成树最近距离
for(int j = 1; j <= v; j++)
{// 加入生成树的条件:// 1. 节点不在生成树中// 2. 距离最小生成树最近的节点if(!isInTree[j] &&  minDist[j] < minDis){minDis = minDist[j];cur = j;	}
}

例如,如下情况:


对于上述情况,节点1,2,3在生成树中,minDist[4] 和 minDist[6]属于minDist[j],内的最小值,所以cur = 4,将节点4加入生成树。(其实加入6也可以,这取决于程序中的 minDist[j] < minDis 处是否取等号)

步骤二:最近节点加入生成树: 就程序而言,加入生成树,就是将最近的节点的生成树标志位立为 true

// 步骤二、最近节点加入生成树
isInTree[cur] = true;

步骤三:更新非生成树节点到生成树的距离: 因为新的节点 cur 加入到了生成树中,那么不在生成树中的节点到最小生成树的距离(即minDist数组需要更新,其中需要更新的节点有以下条件:

  • 节点不在生成树中(在生成树中的当然不用跟新了)
  • 与cur相连的某节点的权值 比 之前 minDist[j]更小了。
// 第三步、更新非生成树节点到生成树的距离
// 那么所有节点到 最小生成树的距离(即minDist数组)需要更新一下
// 由于cur节点是新加入到最小生成树,那么只需要关心与 cur 相连的 非生成树节点 的距离 
// 是否比 原来 非生成树节点到生成树节点的距离更小了呢
for(int j = 1; j <= v; j++)
{// 更新节点的要求:// 1. 节点不在生成树中// 2. 与cur相连的某节点的权值 比 该某节点距离最小生成树的距离小if(!isInTree[j] &&  grid[cur][j] < minDist[j]){minDist[j] = grid[cur][j];}
}

模拟实例如下情况,如图:

  • 此时生成树中:节点1、2、3

  • minDist数组:其中minDist[5] 表示节点5到生成树中节点1的距离

  • 查找最近的节点:节点4

  • 加入新的节点:节点4

  • 新的生成树:节点1、2、3、4

  • 此时由于节点4的加入,节点4到节点5的距离比之前节点5到节点1的距离小 grid[cur][5] < minDist[5],说明点5到最小生成树的距离变小了,需要更新minDist[5],更新minDist数组为:

完整程序:

#include <iostream>
#include <vector>
#include <climits>			// IINT_MAXusing namespace std;int main()
{int v,e;int x,y,k;cin >> v >> e;vector<vector<int>> grid(v+1, vector<int>(v+1, 10001));while(e--){cin >> x >> y >> k;// 因为是双向图,所以两个方向都要填上grid[x][y] = k;grid[y][x] = k;}// 标记节点是否在生成树中vector<bool> isInTree(v+1, false);// 记录非生成树中的节点到生成树的最短距离vector<int> minDist(v+1, 10001);// 只需要循环 n-1次,建立 n - 1条边,就可以把n个节点的图连在一起for(int i = 1; i < v; i++){// 步骤一、选距离生成树最近节点int cur = -1;			// 待加入到生成树中的节点int minDis = INT_MAX;	// 记录本轮离生成树最近距离for(int j = 1; j <= v; j++){// 加入生成树的条件:// 1. 节点不在生成树中// 2. 距离最小生成树最近的节点if(!isInTree[j] &&  minDist[j] < minDis){minDis = minDist[j];cur = j;	}}// 步骤二、最近节点加入生成树isInTree[cur] = true;// 第三步、更新非生成树节点到生成树的距离// cur节点加入之后, 最小生成树加入了新的节点,// 那么所有节点到 最小生成树的距离(即minDist数组)需要更新一下// 由于cur节点是新加入到最小生成树,那么只需要关心与 cur 相连的 非生成树节点 的距离 // 是否比 原来 非生成树节点到生成树节点的距离更小了呢for(int j = 1; j <= v; j++){// 更新节点的要求:// 1. 节点不在生成树中// 2. 与cur相连的某节点的权值 比 该某节点距离最小生成树的距离小if(!isInTree[j] &&  grid[cur][j] < minDist[j]){minDist[j] = grid[cur][j];}}}// 统计结果int result = 0;// 不计第一个顶点,因为统计的是边的权值,v个节点有 v-1条边for (int i = 2; i <= v; i++) { result += minDist[i];}cout << result << endl;
}

拓展

如果让打印出来 最小生成树的每条边呢? 或者说 要把这个最小生成树画出来呢?

使用一维数组记录是有向边,不过我们这里不需要记录方向,所以只关注两条边是连接的就行。

parent数组初始化代码:

vector<int> parent(v + 1, -1);

在更新 minDist数组 的时候,去更新parent数组来记录一下对应的边

for (int j = 1; j <= v; j++) {if (!isInTree[j] && grid[cur][j] < minDist[j]) {minDist[j] = grid[cur][j];parent[j] = cur; 		// 记录最小生成树的边 (注意数组指向的顺序很重要)}
}

代码中 为什么 只能 parent[j] = cur 而不能 parent[cur] = j

  • 如果写成 parent[cur] = j,在 for 循环中,有多个 j 满足要求, 那么 parent[cur] 就会被反复覆盖,因为 cur 是一个固定值。

  • 举个例子,cur = 1, 在 for循环中,可能 就 j = 2, j = 3,j =4 都符合条件,那么本来应该记录 节点1 与 节点 2、节点3、节点4相连的。

  • 如果 parent[cur] = j 这么写,最后更新的逻辑是 parent[1] = 2, parent[1] = 3, parent[1] = 4, 最后只能记录 节点1 与节点 4 相连,其他相连情况都被覆盖了。

  • 如果这么写 parent[j] = cur, 那就是 parent[2] = 1, parent[3] = 1, parent[4] = 1 ,这样 才能完整表示出 节点1 与 其他节点都是链接的,才没有被覆盖。

  • 主要问题也是我们使用了一维数组来记录,如果使用二维数组就不存在这种情况。

最小生成树之kruska(K算法)

K算法也是一种常见的求解最小生成树的方法,prim 算法是维护节点的集合,而 Kruskal 是维护边的集合。

kruscal的思路:

  • 边的权值排序,因为要优先选最小的边加入到生成树里
  • 遍历排序后的边
    • 如果边首尾的两个节点在同一个集合,说明如果连上这条边图中会出现环
    • 如果边首尾的两个节点不在同一个集合,加入到最小生成树,并把两个节点加入同一个集合

过程模拟

依然以示例中,如下这个图来举例。


将图中的边按照权值有小到大排序,这样从贪心的角度来说,优先选 权值小的边加入到 最小生成树中。

排序后的边顺序为:

[(1,2) (4,5) (1,3) (2,6) (3,4) (6,7) (5,7) (1,5) (3,2) (2,4) (5,6)]

开始从头遍历排序后的边

  • 选边(1,2),节点1 和 节点2 不在同一个集合,所以生成树可以添加边(1,2),并将 节点1,节点2 放在同一个集合。

  • 选边(4,5),节点4 和 节点 5 不在同一个集合,生成树可以添加边(4,5) ,并将节点4,节点5 放到同一个集合。

  • 断两个节点是否在同一个集合,就看图中两个节点是否有绿色的粗线连着就行

  • 选边(1,3),节点1 和 节点3 不在同一个集合,生成树添加边(1,3),并将节点1,节点3 放到同一个集合。

  • 选边(2,6),节点2 和 节点6 不在同一个集合,生成树添加边(2,6),并将节点2,节点6 放到同一个集合。

  • 选边(3,4),节点3 和 节点4 不在同一个集合,生成树添加边(3,4),并将节点3,节点4 放到同一个集合。

  • 选边(6,7),节点6 和 节点7 不在同一个集合,生成树添加边(6,7),并将 节点6,节点7 放到同一个集合

  • 选边(5,7),节点5 和 节点7 在同一个集合,不做计算。

  • 选边(1,5),两个节点在同一个集合,不做计算。

  • 后面遍历 边(3,2),(2,4),(5,6) 同理,都因两个节点已经在同一集合,不做计算。

但在代码中,将两个节点加入同一个集合,判断两个节点是否在同一个集合,这就涉及到之前的并查集模板了

程序实现

#include <iostream>
#include <vector>
#include <algorithm>using namespace std;//带权值的边类型 包括起点,终点以及权值
struct Edge
{int v1;int v2;int val;
};
//按边的权值排序仿函数
bool cmp(Edge& edge1, Edge& edge2)	
{return edge1.val < edge2.val;
}// 并查集功能模板
int n = 10001;				// 边树
vector<int> fa(10001);//初始化
void init()
{for(int i = 1; i <= n; i++)fa[i] = i;
}
//查找祖先
int find(int i)
{if(fa[i] == i)return i;else{fa[i] = find(fa[i]);return fa[i];}
}
//加入集合
void join(int u, int v)
{int u_fa = find(u);int v_fa = find(v);if(u_fa == v_fa)return;fa[v_fa] = u_fa;
}
//判断是否在一个集合
bool isSame(int u, int v)
{int u_fa = find(u);int v_fa = find(v);return u_fa == v_fa;
}int main()
{int v, e;cin >> v >> e;//边存储int x, y, k;vector<Edge> edges;		// 边容器while(e--){cin >> x >> y >> k;edges.push_back({x,y,k});}// 将边按权值从小到大排序sort(edges.begin(), edges.end(), cmp);// 并查集初始化init();// 遍历排序后的每条边vector<Edge> result; 	// 存储最小生成树的边for(auto edge: edges){// 加入这条边成环if(isSame(edge.v1, edge.v2))continue;else{join(edge.v1, edge.v2);result.push_back(edge);}}// 计算生成树大小int res = 0;for(auto edge: result)res += edge.val;cout << res << endl;
}

拓展

输出最小生成树的边

因为K算法本身就是保存的边,因此在判断加入的2个点不在一个集合的时候,将对应的边保存在结果集中即可。

当判断两个节点不在同一个集合的时候,这两个节点的边就加入到最小生成树, 所以添加边的操作在这里:

// 遍历排序后的每条边
vector<Edge> result; 	// 存储最小生成树的边
for(auto edge: edges)
{// 加入这条边成环if(isSame(edge.v1, edge.v2))continue;else{join(edge.v1, edge.v2);		// 两个节点加入到同一个集合result.push_back(edge);		// 记录最小生成树的边}
}

Kruskal 与 prim 算法的区别:

  • Kruskal 与 prim 的关键区别在于,prim维护的是节点的集合,而 Kruskal 维护的是边的集合
  • 如果 一个图中,节点多,但边相对较少,那么使用Kruskal 更优
  • 而 prim 算法是对节点进行操作的,节点数量越少,prim算法效率就越优。

拓扑排序

卡码网:117. 软件构建

拓扑排序:将图的点按先后顺序进行排序

示例1:


则拓扑排序有:

a e b c d
a b e c d
a b c e d

以上三种排序中任意一种即可。

示例2:


输出示例:

0 1 2 3 4

顺序除了示例中的顺序,还存在

0 2 4 1 3

0 2 1 3 4

等等合法的顺序。

示例3:

输出:

-1

不能成功处理(相互依赖,存在环),输出 -1。

背景与思路

拓扑排序的背景

  • 大学排课,例如 先上A课,才能上B课,上了B课才能上C课,上了A课才能上D课,等等一系列这样的依赖顺序。 问给规划出一条 完整的上课顺序
  • 概括来说,给出一个 有向图,把这个有向图转成线性的排序 就叫拓扑排序
  • 当然拓扑排序也要检测这个有向图 是否有环,即存在循环依赖的情况,因为这种情况是不能做线性排序的。
  • 所以拓扑排序也是图论中判断有向无环图的常用方法。

拓扑排序的思路

实现拓扑排序的算法有两种:卡恩算法(BFS) 和 DFS

所以当我们做拓扑排序的时候,应该优先找 入度为 0 的节点,只有入度为0,它才是出发节点。

拓扑排序的过程,其实就两步:

  • 找到入度为0 的节点,加入结果集
  • 将该节点从图中移除

循环以上两步,直到 所有节点都在图中被移除。

模拟过程

用本题的示例2来模拟这一过程:

1. 找到入度为0 的节点,加入结果集  2. 将该节点从图中移除

1. 找到入度为0 的节点,加入结果集  2. 将该节点从图中移除

这里节点1 和 节点2 入度都为0, 选哪个都行,所以这也是为什么拓扑排序的结果是不唯一的。

1. 找到入度为0 的节点,加入结果集  2. 将该节点从图中移除

节点2 和 节点3 入度都为0,选哪个都行,这里选节点2

后面的过程一样的,节点3 和 节点4,入度都为0,选哪个都行。

最后结果集为: 0 1 2 3 4 。当然结果不唯一的。

判断有环

如果有 有向环怎么办呢?例如这个图:


那么如果我们发现结果集元素个数 不等于 图中节点个数,我们就可以认定图中一定有 有向环!

这也是拓扑排序判断有向环的方法。

程序实现

(1)为了每次可以找到所有节点的入度信息,我们要在初始化的时候,就把每个节点的入度 和 每个节点的依赖关系做统计。

int n, m;
cin >> n >> m;int s, t;
vector<int> inDegree(n, 0); 			// 记录每个节点的入度
vector<int> result;  					// 记录结果排序集
unordered_map<int, vector<int>> umap;	// 记录依赖关系
while(m--)
{cin >> s >> t;inDegree[t]++;				// s->t t的入度++umap[s].push_back(t);		// 记录节点s指向哪些节点 建立依赖
}

(2)找入度为0 的节点,我们需要用一个队列放存放。
  因为每次寻找入度为0的节点,不一定只有一个节点,可能很多节点入度都为0,所以要将这些入度为0的节点放到队列里,依次去处理。

//找入度为0 的节点,我们需要用一个队列放存放。
//因为每次寻找入度为0的节点,不一定只有一个节点,可能很多节点入度都为0,
//所以要将这些入度为0的节点放到队列里,依次去处理。
queue<int> que;
for(int i = 0; i < n; i++)
{if(inDegree[i] == 0)que.push(i);
}

(3)开始从队列里遍历入度为0 的节点,将其放入结果集。

while(que.size())
{int  cur = que.front(); // 当前选中的节点que.pop();result.push_back(cur);// 将该节点从图中移除 }

(4)删除入度为0的节点以及相连的边
  删除节点目的是为了删除相连的边,删除相连的边是为了所连接的节点的入度 减一,从而继续判断留下节点的度数是否有为0,有则加入队列,作为下一轮起点
模拟过程第一步:开始时候节点1和节点2的度数为1,当删除节点0后,节点1和节点2的度数变成0,加入队列。

//队列不为空 存在入度0的节点
while(que.size())
{int  cur = que.front(); // 当前选中的节点que.pop();result.push_back(cur);	//当前节点加入结果集vector<int> nodes = umap[cur];	//获取当前节点所连接的节点// cur后续有连接的节点if(nodes.size()){//顺序处理这些连接的节点for (int i = 0; i < nodes.size(); i++) {// cur的指向的文件入度-1inDegree[nodes[i]]--;// 如果入度减到0, 加入队列if(inDegree[nodes[i]] == 0)que.push(nodes[i]);}}	
}

(5)结果输出(判断是否成环)

	if( n == result.size()){for(int i = 0; i < n-1;i++)cout << result[i] << " ";cout << result[n-1];}elsecout << -1 << endl;

完成程序实现:

#include <iostream>
#include <unordered_map>
#include <vector>
#include <queue>using namespace std;int main()
{int n, m;cin >> n >> m;int s, t;vector<int> inDegree(n, 0); 			// 记录每个节点的入度vector<int> result;  					// 记录结果排序集unordered_map<int, vector<int>> umap;	// 记录依赖关系while(m--){cin >> s >> t;inDegree[t]++;				// s->t t的入度++umap[s].push_back(t);		// 记录节点s指向哪些节点 建立依赖}//找入度为0 的节点,我们需要用一个队列放存放。//因为每次寻找入度为0的节点,不一定只有一个节点,可能很多节点入度都为0,//所以要将这些入度为0的节点放到队列里,依次去处理。queue<int> que;for(int i = 0; i < n; i++){if(inDegree[i] == 0)que.push(i);}//队列不为空 存在入度0的节点while(que.size()){int  cur = que.front(); // 当前选中的节点que.pop();result.push_back(cur);	//当前节点加入结果集vector<int> nodes = umap[cur];	//获取当前节点所连接的节点// cur后续有连接的节点if(nodes.size()){//顺序处理这些连接的节点for (int i = 0; i < nodes.size(); i++) {// cur的指向的文件入度-1inDegree[nodes[i]]--;// 如果入度减到0, 加入队列if(inDegree[nodes[i]] == 0)que.push(nodes[i]);}}	}if( n == result.size()){for(int i = 0; i < n-1;i++)cout << result[i] << " ";cout << result[n-1];}elsecout << -1 << endl;return 0;
}

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

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

相关文章

『完整代码』坐骑召唤

创建一个按钮 作为召唤/消失坐骑的开关 将预制体放入指定文件夹 命名为Mount01 创建脚本并编写&#xff1a;CallMount.cs using UnityEngine; using UnityEngine.UI; public class CallMount : MonoBehaviour{public Button callBtn;GameObject mountPrefab;GameObject mountIn…

CentOS7 上安装GitLab的经历

一、安装必要的基础环境 1.安装依赖包 [rootgitlab-server ~]#yum install curl policycoreutils openssh-server openssh-clients postfix wget git patch -y [rootgitlab-server ~]# systemctl start postfix 2.配置yum源(由于网络问题&#xff0c;国内用户请使用清华大学…

导数的概念及在模型算法中的应用

一. 导数概念与计算 1. 导数的物理意义&#xff1a; 瞬时速率。一般的&#xff0c;函数yf(x)在x处的瞬时变化率是 2. 导数的几何意义&#xff1a; 曲线的切线&#xff0c;当点趋近于P时&#xff0c;直线 PT 与曲线相切。容易知道&#xff0c;割线的斜率是当点趋近于 P 时&…

数字孪生:引领智慧农业的未来

在现代农业中&#xff0c;数字化与智能化的浪潮正在改变传统的种植方式。数字孪生技术作为一种创新的数字化解决方案&#xff0c;正在深刻改变智慧农业的面貌&#xff0c;尤其是在大棚智能控制、数据全面可视、加工过程监控和物流运输溯源等方面展现出巨大的潜力。 frontop数字…

【动手学深度学习】8.1. 序列模型(个人向笔记)

想象一下有人正在看网飞&#xff08;Netflix&#xff0c;一个国外的视频网站&#xff09;上的电影。 一名忠实的用户会对每一部电影都给出评价&#xff0c; 毕竟一部好电影需要更多的支持和认可。 然而事实证明&#xff0c;事情并不那么简单。 随着时间的推移&#xff0c;人们对…

《Python基础教程》笔记(ch0-1)

前言 在Python生态系统中&#xff0c;各种包轮番登场&#xff0c;各种编码实践大行其道后又日渐式微。 引言 Python是什么&#xff1f;为何要使用它&#xff1f;官方宣传说&#xff1a;Python是一种面向对象的解释性高级编程语言&#xff0c;具有动态语义。 这句话的要点在…

监控易DEMO功能深度解析:运维行业的智能化转型新助力

在数字化转型的浪潮中&#xff0c;运维行业正面临着前所未有的变革与挑战。为了应对日益复杂的IT架构和不断提升的运维需求&#xff0c;监控易的集中式跨平台一体化监控软件不断升级优化&#xff0c;以适应新的运维环境。本文将对监控易DEMO的功能进行深度解析&#xff0c;探讨…

简单介绍冯诺依曼体系

现代的计算机, 大多遵守冯诺依曼体系结构 CPU中央处理器&#xff1a;进行算术运算和逻辑判断。存储器&#xff1a;分为外存和内存&#xff0c;用于存储数据&#xff08;使用二进制方式存储&#xff09;。输入设备&#xff1a;用户给计算机发号施令。输出设备&#xff1a;计算机…

Hadoop生态圈三大组件:HDFS的读写流程、MapReduce计算流程、Yarn资源调度

文章目录 1. HDFS的读写流程1.1 HDFS读流程1.2 HDFS写流程 2. MapReduce计算流程3. Yarn资源调度一、客户端请求资源二、Resource Manager处理请求三、任务资源计算与申请四、Resource Manager分配资源五、Node Manager执行任务六、任务执行与监控 1. HDFS的读写流程 1.1 HDFS…

沃德商协会管理系统小程序源码

商协会管理系统小程序&#xff0c;作为新一代数字化商协会运营管理的先锋工具&#xff0c;其核心围绕“智慧化会员体系、智敏化内容运营、智能化活动构建”三大核心板块精心构建。这一系统通过智慧化会员体系&#xff0c;实现了会员信息的精准管理与高效互动&#xff0c;不仅简…

2024_E_100_连续字母长度

连续字母长度 题目描述 给定一个字符串&#xff0c;只包含大写字母&#xff0c;求在包含同一字母的子串中&#xff0c;长度第 k 长的子串的长度&#xff0c;相同字母只取最长的那个子串。 输入描述 第一行有一个子串(1<长度<100)&#xff0c;只包含大写字母。 第二行为…

Rancher2.6管理k8s1.23

Rancher2.6管理k8s1.23 简介Rancher和k8s的区别 安装rancher初始化实验环境新增hosts文件条目安装docker 安装Rancher登录Rancher平台 通过Rancher仪表盘管理k8s集群&#xff1a;部署tomcat服务创建Ingress资源创建ingress规则 简介 Rancher是一个开源的企业级多集群Kubernete…

HarmonyOS 开发知识总结

1. HarmonyOS 开发知识总结 1.1. resources->base->media中不可以新建文件夹&#xff1f; 项目图片路径resources->base->media中不可以新建文件夹&#xff0c;图片全平级放里面&#xff0c;查找图片不方便&#xff0c;有没有什么其他的办法解决这个难点&#xff…

Scala入门基础(12)抽象类

抽象类&#xff0c;制定标准&#xff0c;不要求去具体实现 包含了抽象方法的类就是抽象类。抽象方法只是有方法名&#xff0c;没有具体方法体的方法 定义抽象类要用abstract&#xff08;抽象&#xff09;关键字 用智能驾驶技术举例&#xff1a;演示&#xff09…

干货|基于Taro框架开发微信小程序如何配置实现自动格式化和代码规范

下面实例是基于 Taro框架使用React开发微信小程序的实现自动格式化和代码规范的配置教程 安装 ESLint 和 Prettier 插件&#xff1a; 在微信开发者工具的插件市场中搜索并安装 ESLint 和 Prettier 插件。 配置 .eslintrc.js 文件&#xff1a; 确保项目根目录下有一个 .eslint…

简单介绍$listeners

$listeners 它可以获取父组件传递过来的所有自定义函数&#xff0c;如下&#xff1a; // 父组件 <template><div class"a"><Child abab"handleAbab" acac"handleAcac"/></div> </template><script> impor…

on hold 的SSCI期刊,一般多久会解除?

SSCI期刊“On Hold”状态解析及其解除时间概览 在学术研究的广阔领域中&#xff0c;发表论文不仅是知识分享的重要途径&#xff0c;也是衡量研究成果影响力的关键指标之一。对于社会科学领域的学者而言&#xff0c;SSCI&#xff08;Social Sciences Citation Index&#xff09…

分布式机器学习模式 精彩试读

近年来&#xff0c;机器学习取得了巨大进步&#xff0c;但大规模机器学习仍然面临挑战。 以 模型训练为例&#xff0c;由于 TensorFlow、PyTorch 和 XGBoost 等机器学习框架具有多 样性&#xff0c;从而使得在分布式 Kubernetes 集群上自动化训练机器学习模型的过程并 不简单。…

力扣动态规划基础版(斐波那契类型)

70. 爬楼梯https://leetcode.cn/problems/climbing-stairs/ 70.爬楼梯 方法一 动态规划 考虑转移方程和边界条件&#xff1a; f&#xff08;x&#xff09; f&#xff08;x -1&#xff09; f&#xff08;x - 2&#xff09;;f&#xff08;1&#xff09; 1&#xff1b;f&…

NeRF三维重建—神经辐射场Neural Radiance Field(二)体渲染相关

NeRF三维重建—神经辐射场Neural Radiance Field&#xff08;二&#xff09;体渲染相关 粒子采集部分 粒子采集的部分我们可以理解为&#xff0c;在已知粒子的情况下&#xff0c;对图片进行渲染的一个正向的过程。 空间坐标(x,y,z&#xff09;发射的光线通过相机模型成为图片上…