【算法模板】图论基础算法

文章目录

  • 图论算法基础模板
      • 树与图的存储
        • 1. 邻接矩阵:
        • 2. 邻接表:
      • 树与图的遍历
        • (1)深度优先搜索 (DFS)
        • 深度优先遍历 (DFS)
        • (2)宽度优先搜索 (BFS)
        • 宽度优先遍历 (BFS)
      • 拓扑排序
      • 朴素Dijkstra算法
      • 堆优化版Dijkstra算法
      • Bellman-Ford算法
      • SPFA算法
      • SPFA判断图中是否存在负环
      • Floyd算法
      • 朴素版Prim算法
      • Kruskal算法
      • 染色法判别二分图
      • 匈牙利算法


图论算法基础模板

  • 是一种特殊的图,因此它们可以使用相同的存储方式。我们这里主要考虑有向图的存储方法。

树与图的存储

树是一种特殊的图,因此它们可以使用相同的存储方式。我们这里主要考虑有向图的存储方法。

1. 邻接矩阵:

在邻接矩阵中,使用二维数组 g[a][b] 来表示边 a->b

2. 邻接表:

对于每个点 k,开一个单链表,存储所有可以从 k 出发到达的点。h[k] 存储这个单链表的头结点。

添加一条边 a->b 时,将 b 添加到以 a 为头结点的链表中。

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

// 添加一条边a->b
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

// 初始化
idx = 0;
memset(h, -1, sizeof h);

这种存储方式对于表示树和图都是有效的,通过邻接表的形式可以灵活地添加和删除边,适用于各种图论算法的实现。

树与图的遍历

(1)深度优先搜索 (DFS)

深度优先搜索是一种通过递归或栈实现的遍历算法。从起始顶点开始,尽可能深地访问每个顶点,直到到达终点或无法继续深入。

  • 算法步骤:

    1. 从起始顶点开始,将其标记为已访问。
    2. 对于起始顶点的每个邻接顶点,如果该邻接顶点未被访问,则递归调用 DFS 函数进行访问。
    3. 重复步骤 2,直到无法继续深入为止。
  • 时间复杂度:O(n+m),其中 n 表示点数,m 表示边数。

深度优先遍历 (DFS)
void dfs(int u)
{
    st[u] = true; // st[u] 表示点 u 已经被遍历过
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}
(2)宽度优先搜索 (BFS)

宽度优先搜索是一种通过队列实现的遍历算法。从起始顶点开始,逐层地访问与其相邻的顶点,确保每个顶点只被访问一次。

  • 算法步骤:
    1. 将起始顶点放入队列,并标记为已访问。
    2. 从队列中取出一个顶点,并访问其所有未被访问的邻接顶点,将其放入队列并标记为已访问。
    3. 重复步骤 2,直到队列为空。
  • 时间复杂度:O(n+m),其中 n 表示点数,m 表示边数。
宽度优先遍历 (BFS)
queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (!q.empty())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点 j 已经被遍历过
            q.push(j);
        }
    }
}

拓扑排序

拓扑排序是对有向无环图进行排序的算法,它确定顶点的线性顺序,使得对于图中的每一对有向边 (u, v),顶点 u 都排在顶点 v 的前面。

  • 算法步骤:

    1. 计算每个顶点的入度,即指向该顶点的边的数量。
    2. 将所有入度为 0 的顶点加入拓扑序列,并将与这些顶点相邻的边的入度减 1。
    3. 重复步骤 2,直到所有顶点都加入拓扑序列或者不存在入度为 0 的顶点为止。
  • 时间复杂度:O(n+m),其中 n 表示点数,m 表示边数。

bool topsort()
{
    int hh = 0, tt = -1;
    // d[i] 存储点 i 的入度
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[++tt] = i;

    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)
                q[++tt] = j;
        }
    }
    // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
    return tt == n - 1;
}

朴素Dijkstra算法

朴素 Dijkstra 算法是一种贪心算法,用于求解单源最短路径问题。从起始顶点开始,每次选择距离最短的顶点进行扩展,并更新其他顶点的距离。

  • 算法步骤:

    1. 初始化距离数组,将起始顶点到其他顶点的距离初始化为无穷大。
    2. 将起始顶点的距离设置为 0,并将其加入集合。
    3. 对于集合中的每个顶点,更新其相邻顶点的距离。
    4. 重复步骤 3,直到集合为空。
  • 时间复杂度:O(n^2+m),其中 n 表示点数,m 表示边数。

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n - 1; i ++ )
    {
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        // 用 t 更新其他点的距离
        for (int j = 1; j <= n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        st[t] = true;
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

堆优化版Dijkstra算法

堆优化 Dijkstra 算法是一种改进的 Dijkstra 算法,旨在加快寻找当前距离最小的顶点的过程。通过使用优先队列(通常实现为最小堆),算法可以在每次迭代中快速找到下一个最优的顶点,而不是遍历所有顶点进行比较。

  • 算法步骤:

    1. 初始化距离数组, 将所有顶点的距离初始化为无穷大,表示当前距离未知。
    2. 将起始顶点加入优先队列, 将起始顶点的距离设置为 0,并将其加入优先队列中。
    3. 从优先队列中取出距离最小的顶点,每次从优先队列中取出距离最小的顶点,这个顶点的距离已经确定为最小值。
    4. 更新相邻顶点的距离,对于当前顶点的所有相邻顶点,如果通过当前顶点到达它们的路径比已知的距离更短,则更新它们的距离,并将其加入优先队列中。
    5. 重复步骤 3 和步骤 4: 不断从优先队列中取出距离最小的顶点,并更新其相邻顶点的距离,直到优先队列为空。
  • 时间复杂度:O(mlogn),其中 n 表示点数,m 表示边数。

int dijkstra()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, 1});      // first存储距离,second存储节点编号

    while (!heap.empty())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.second, distance = t.first;

        if (st[ver]) continue;
        st[ver] = true;

        for (int i = h[ver]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > distance + w[i])
            {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }

    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

Bellman-Ford算法

Bellman-Ford 算法是一种用于解决单源最短路径问题的算法,能够处理带有负权边的图。通过动态规划的思想,算法不断迭代缩小顶点到达目标顶点的距离估计,直到收敛为止。

  • 算法步骤:

    1. 初始化距离数组, 将起始顶点到其他顶点的距离初始化为无穷大,除了起始顶点本身的距离为 0。
    2. 进行 n-1 次松弛操作,对于每一条边 (u, v),如果通过顶点 u 到达顶点 v的路径比当前已知的路径更短,则更新顶点 v的距离为通过顶点 u 到达 v 的距离。
    3. 检查负权环, 进行了 n-1 次松弛操作后,再进行一次松弛操作。如果在这一次操作中仍然发生了距离的更新,则说明图中存在负权环。
    4. 返回最短路径,如果不存在负权环,最终得到的距离数组就是起始顶点到各个顶点的最短路径长度。
  • 时间复杂度:O(nm),其中 n 表示点数,m 表示边数。

int bellman_ford()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int a = edges[j].a, b = edges[j].b, w = edges[j].w;
            if (dist[b] > dist[a] + w)
                dist[b] = dist[a] + w;
        }
    }

    if (dist[n] > 0x3f3f3f3f / 2) return -1;
    return dist[n];
}

SPFA算法

SPFA(Shortest Path Faster Algorithm)是一种单源最短路径算法,用于求解带有负权边但无负权环的图的最短路径。其基本思想是类似于Bellman-Ford算法,但是SPFA通过维护一个队列来减少不必要的松弛操作,从而提高了效率。

算法步骤:

  1. 将起点加入队列,并标记为已访问。
  2. 不断从队首取出一个点,对其相邻的未访问点进行松弛操作,更新最短路径。
  3. 如果某个点的最短路径发生了改变,且该点不在队列中,则将其加入队列,并标记为已访问。
  4. 重复步骤2和步骤3,直到队列为空。

如果在算法执行过程中,某个点入队次数超过了n次,说明存在负权环。

  • 时间复杂度:平均情况下 O(m),最坏情况下 O(nm),其中 n 表示点数,m 表示边数。
int spfa()
{
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;

    queue<int> q;
    q.push(1);
    st[1] = true;

    while (!q.empty())
    {
        int t = q.front();
        q.pop();
        
		st[t] = false;
        for (int i = h[t]; i != -1; i = ne[i])
    	{
        	int j = e[i];
        	if (dist[j] > dist[t] + w[i])
        	{
           		 dist[j] = dist[t] + w[i];
            	if (!st[j])     // 如果队列中已存在 j,则不需要将 j 重复插入
            	{
                q.push(j);
                st[j] = true;
           		}
       		 }
        }
    }

   if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];   
}

SPFA判断图中是否存在负环

  • 时间复杂度:O(nm),其中 n 表示点数,m 表示边数。
bool spfa()
{
    queue<int> q;
    for (int i = 1; i <= n; i ++ )
    {
        q.push(i);
        st[i] = true;
    }

    while (!q.empty())
    {
        int t = q.front();
        q.pop();

        st[t] = false;

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[t] + w[i])
            {
                dist[j] = dist[t] + w[i];
                cnt[j] = cnt[t] + 1;
                if (cnt[j] >= n) return true;       // 如果从1号点到 x 的最短路中包含至少 n 个点(不包括自己),则说明存在环
                if (!st[j])
                {
                    q.push(j);
                    st[j] = true;
                }
            }
        }
    }

    return false;
}

Floyd算法

Floyd算法是一种经典的多源最短路径算法,用于求解图中任意两点之间的最短路径。其核心思想是动态规划,通过中转点的引入逐步优化路径的长度。

算法步骤:

  1. 初始化任意两点之间的最短路径长度。
  2. 逐步考虑每个点作为中转点时,更新任意两点之间的最短路径长度。
  3. 重复步骤2,直到所有点都考虑过。

Floyd算法的核心操作是三重循环,分别遍历所有点对和中转点,通过比较经过中转点和不经过中转点的路径长度来更新最短路径。

  • 时间复杂度:O(n^3),其中 n 表示点数。
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

朴素版Prim算法

Prim算法是一种用于求解最小生成树的贪心算法,其基本思想是从一个初始点出发,逐步选取与当前最小生成树连接的最短边所连接的点,直到所有点都被连接为止。

算法步骤:

  1. 选择一个初始点作为最小生成树的根节点。
  2. 从已选取的点集合中选取与之相邻的边中权值最小的边所连接的点,加入到最小生成树中。
  3. 重复步骤2,直到所有点都被加入到最小生成树中。

Prim算法可以通过邻接矩阵或邻接表来实现。邻接矩阵的实现简单直观,但对于稀疏图效率较低,而邻接表适合稀疏图的情况。

  • 时间复杂度:O(n^2+m),其中 n 表示点数,m 表示边数。
int prim()
{
    memset(dist, 0x3f, sizeof dist);

    int res = 0;
    for (int i = 0; i < n; i ++ )
    {
        int t = -1;
        for (int j = 1; j <= n; j ++ )
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;

        if (i && dist[t] == 0x3f3f3f3f) return INF;

        if (i) res += dist[t];
        st[t] = true;

        for (int j = 1; j <= n; j ++ ) dist[j] = min(dist[j], g[t][j]);
    }

    return res;
}

Kruskal算法

Kruskal算法是一种用于求解最小生成树的贪心算法,其基本思想是先将所有边按照权值从小到大排序,然后逐步选取权值最小且不构成环的边加入到最小生成树中。

算法步骤:

  1. 将所有边按照权值从小到大排序。
  2. 依次考虑每条边,如果该边连接的两个端点不在同一连通块中,则将该边加入到最小生成树中,并将两个端点合并为一个连通块。
  3. 重复步骤2,直到所有点都被连接为止。

Kruskal算法通常使用并查集来实现,用于判断两个点是否属于同一个连通块,并在加入边时实现快速的合并操作。

  • 时间复杂度:O(mlogm),其中 n 表示点数,m 表示边数。
int kruskal()
{
    sort(edges, edges + m);

    for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集

    int res = 0, cnt = 0;
    for (int i = 0; i < m; i ++ )
    {
        int a = edges[i].a, b = edges[i].b, w = edges[i].w;

        a = find(a), b = find(b);
        if (a != b)     // 如果两个连通块不连通,则将这两个连通块合并
        {
            p[a] = b;
            res += w;
            cnt ++ ;
        }
    }

    if (cnt < n - 1) return INF;
    return res;
}

染色法判别二分图

染色法是一种用于判别图是否为二分图的常用算法。二分图是一种特殊的图,可以将所有节点分为两个不相交的集合,并且图中的每条边都连接一端点属于其中一个集合,另一端点属于另一个集合。染色法通过遍历图中的节点,并对每个节点进行染色,使得相邻节点的颜色不同,如果成功染色,则说明图是二分图。

算法步骤:

  1. 从任意一个未染色的节点开始,将其染色为0。
  2. 遍历该节点的所有相邻节点,如果相邻节点未被染色,则将其染色为与当前节点不同的颜色(比如1)。
  3. 对相邻节点递归执行步骤2。
  4. 如果在染色的过程中出现了相邻节点颜色相同的情况,则说明图不是二分图。

染色法的时间复杂度为O(n+m),其中n表示节点数,m表示边数。它的实现比较简单,通常使用递归或队列等方式进行遍历和染色操作。

  • 时间复杂度:O(n+m),其中 n 表示点数,m 表示边数。
bool dfs(int u, int c)
{
    color[u] = c;
    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (color[j] == -1)
        {
            if (!dfs(j, !c)) return false;
        }
        else if (color[j] == c) return false;
    }

    return true;
}

bool check()
{
    memset(color, -1, sizeof color);
    bool flag = true;
    for (int i = 1; i <= n; i ++ )
        if (color[i] == -1)
            if (!dfs(i, 0))
            {
                flag = false;
                break;
            }
    return flag;
}

匈牙利算法

匈牙利算法是一种用于求解二分图最大匹配的经典算法。最大匹配是指在一个二分图中,找到尽可能多的边,使得每个节点最多只与一个节点相连。

算法步骤:

  1. 初始化一个空的匹配集合。
  2. 从左边的未匹配节点开始,依次寻找与其相邻的右边未匹配节点。
  3. 如果找到了未匹配的右边节点,则将其加入匹配集合中,并继续尝试匹配其他节点。
  4. 如果无法找到未匹配的右边节点,则回溯到之前的匹配状态,并尝试匹配其他左边节点。
  5. 重复步骤2到步骤4,直到所有左边节点都尝试过匹配。

匈牙利算法通常使用DFS或BFS进行实现,其中DFS更为常见。其时间复杂度为O(nm),其中n表示左边节点数,m表示右边节点数。

  • 时间复杂度:O(nm),其中 n1 表示第一个集合中的点数,n2 表示第二个集合中的点数。
bool find(int x)
{
    for (int i = h[x]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true;
            if (match[j] == 0 || find(match[j]))
            {

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言:一维数组、二维数组、字符数组介绍

数组 介绍一维数组定义应用方法初始化 举例示例结果 二维数组定义应用方法初始化 举例示例结果 字符数组定义应用方法初始化 举例示例结果分析 介绍 在C语言中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储一系列相同类型的数据。数组可以是多维的&#xff0c;最…

phpstorm 设置变量,自动补全代码

效果 进入设置->实时模板->PHP->添加 添加动态模板->完善写法 定义->选择PHP->应用就行

什么是宏观经济的先行指标、同步指标与滞后指标

宏观经济波动是一种周期性的繁荣、衰退、萧条、复苏循环变化过程&#xff0c;在这种变动中&#xff0c;不同经济指标的变动并非总与宏观经济运行步调一致。按统计指标变动轨迹与宏观经济变动轨迹的时间关系,可以将其划分为先行指标、同步指标和滞后指标。 一、概念和作用 先行…

JetBrains CLion v2023.3.4 激活版 (C/C++ 集成开发IDE)

前言 JetBrains CLion是一款跨平台的C/C集成开发环境&#xff0c;由JetBrains公司推出。其最新版本支持C14几乎完全&#xff0c;并初步支持C17&#xff0c;使得编写代码更加便捷。CLion还提供了Disassembly view&#xff08;反汇编视图&#xff09;&#xff0c;即使没有源代码…

《欢乐钓鱼大师》攻略:怎么在竞标赛中获得高分?

《欢乐钓鱼大师》锦标赛是游戏中的一项激动人心的钓鱼比赛活动&#xff0c;而在这场比赛中&#xff0c;如何获得高分成为了每位钓手追求的目标。在这篇攻略中&#xff0c;我们将为您详细介绍如何通过优化鱼竿、管理体力、利用buff和词条以及前期准备等方面来提高您在锦标赛中的…

信号分解 | RLMD(鲁棒性局部均值分解)-Matlab

分解效果 RLMD(鲁棒性局部均值分解) RLMD(鲁棒性局部均值分解)-Matlab 代码实现 % %% 清除所有变量 关闭窗口 clc clear all close all%% 导入数据 % data = xlsread(Data.xlsx);%% 输入信号%% RLMD分解 %参数进行设置 % options.display =

【React】CSS 局部样式

书写 CSS 的时候&#xff0c;如果 CSS 文件名包含 module&#xff0c;那么说明该 CSS 是一个局部 CSS 样式文件&#xff0c;类似于 vue 中的 scoped。 .avatarContainer {width: 40px;height: 40px;border-radius: 50%;background: rgb(213, 226, 226); }import styles from ..…

【Redis 开发】缓存雪崩和缓存击穿

缓存问题 缓存雪崩解决方案 缓存击穿互斥锁逻辑时间基于互斥锁解决缓存击穿问题基于逻辑过期方式解决缓存击穿问题 缓存雪崩 缓存雪崩是指在同一时间段&#xff0c;大量的缓存key同时失效或者Redis服务器宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力 解决…

游戏发行困境及OgGame云游戏解决方案简述

随着全球化浪潮的持续推进&#xff0c;中国游戏开发者们不再满足于国内市场的发展&#xff0c;而是开始将目光投向更为广阔的海外市场。这一趋势的崛起背后&#xff0c;是中国企业意识到国际化是其发展的必由之路&#xff0c;也是游戏行业突破国内困境的体现。本文将简要阐述游…

【1731】jsp 房租跟踪监控管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 房租跟踪监控管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysq…

为什么常用氢化物

知识星球&#xff08;星球名&#xff1a;芯片制造与封测社区&#xff09;里的学员问&#xff1a;diffusion工序&#xff0c;所需要的气体种类有哪些&#xff1f; Diffusion是什么工序&#xff1f; "Diffusion"工序是通过热能将掺杂剂原子扩散到硅片中&#xff0c;以形…

C++系列-输入输出

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” C输入和输出 我们都知道C语言的输出是用printf函数来实现的&#xff0c;那么C呢&#xff0c;它的实现逻辑是什么呢&#xff0c;让我们一起来看一下&#xff0c; #include<i…

【代码随想录刷题记录】LeetCode27移除元素

题目地址 1. 思路 1.1 基本思路及代码的初步实现 基本思路大体上和卡尔老师的想法是一致的&#xff0c;详见代码随想录&#xff1a;数组&#xff1a;移除元素&#xff0c;暴力法大家都能想到&#xff0c;我这里写一下算法时间复杂度为 O ( n ) O(n) O(n)时候的思路&#xff…

【深度学习】YOLOv5,烟雾和火焰,目标检测,防火检测,森林火焰检测

文章目录 数据收集和数据标注查看标注好的数据的脚本下载yolov5创建 dataset.yaml训练参数开始训练yolov5n训练训练后的权重下载gradio部署 数据收集和数据标注 搜集数据集2w张。 pip install labelme labelme 然后标注矩形框和类别。 下载数据请看这里&#xff1a; https:…

2023年江西省电子专题赛——解析一

由于网上对这个竞赛资料甚少&#xff0c;为了方便省内学子交流学习&#xff0c;可加Q群聊&#xff1a;778772385 电源部分&#xff1a;比赛中只给了3个IN4007整流管&#xff0c;无法构成传统整流桥的形式&#xff0c;并且题目又要求全波整流。 我这边用两个二极管构成全波整流…

Web3解密:理解去中心化应用的核心原理

引言 在当前数字化时代&#xff0c;去中心化技术和应用正在逐渐引起人们的关注和兴趣。Web3技术作为去中心化应用&#xff08;DApps&#xff09;的基础&#xff0c;为我们提供了一个全新的互联网体验。但是&#xff0c;对于许多人来说&#xff0c;这个复杂的概念仍然充满了神秘…

【数据结构与算法】:手搓顺序表(Python篇)

文章目录 一、顺序表的概念二、顺序表的实现1. 顺序表的创建1.1 扩容1.2 整体建立顺序表 2. 顺序表的基本运算算法2.1 顺序表的添加&#xff08;尾插&#xff09;2.2 指定位置插入2.3 指定位置删除2.4 顺序表的查找2.5 顺序表元素的索引访问2.6 顺序表元素的修改2.7 顺序表长度…

Mouse without Borders(无界鼠标)使用教程 多台电脑(最多4)共用鼠标键盘,换言之一套键鼠操作多台电脑,跨电脑文件拖动传输

Mouse without Borders&#xff08;无界鼠标&#xff09;使用教程 目的&#xff1a;多台电脑&#xff08;最多4&#xff09;共用鼠标键盘&#xff0c;换言之一套键鼠操作多台电脑。 优势&#xff1a;微软官方软件&#xff0c;对于windows系统友好&#xff0c;轻量实用。 劣势…

【Diffusion实战】训练一个diffusion模型生成蝴蝶图像(Pytorch代码详解)

上一篇Diffusion实战是确确实实一步一步走的公式&#xff0c;这回采用一个更方便的库&#xff1a;diffusers&#xff0c;来实现Diffusion模型训练。 Diffusion实战篇&#xff1a;   【Diffusion实战】训练一个diffusion模型生成S曲线&#xff08;Pytorch代码详解&#xff09;…

锁,数据同步

目录 原子操作中断控制自旋锁信号量小结 经常遇到数据同步的问题&#xff0c;具体有哪些情况呢&#xff1f;来聊一聊。我知道的&#xff0c;应该有以下四种&#xff0c;原子操作&#xff0c;中断控制&#xff0c;自旋锁和信号量。 原子操作 适用情况和场景 原子操作经常用在单…