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

2304. 网格中的最小路径代价 : 从「图论最短路」过渡到「O(1) 空间的原地模拟」

题目描述

这是 LeetCode 上的 「2304. 网格中的最小路径代价」 ,难度为 「中等」

Tag : 「最短路」、「图」、「模拟」、「序列 DP」、「动态规划」

给你一个下标从 0 开始的整数矩阵 grid,矩阵大小为 m x n,由从 0 的不同整数组成。

你可以在此矩阵中,从一个单元格移动到下一行的任何其他单元格。

如果你位于单元格 ,且满足 ,你可以移动到 , , ..., 中的任何一个单元格。注意: 在最后一行中的单元格不能触发移动。

每次可能的移动都需要付出对应的代价,代价用一个下标从 0 开始的二维数组 moveCost 表示,该数组大小为 ,其中 moveCost[i][j] 是从值为 i 的单元格移动到下一行第 j 列单元格的代价。从 grid 最后一行的单元格移动的代价可以忽略。

grid 一条路径的代价是:所有路径经过的单元格的值之和加上所有移动的代价之和 。从第一行任意单元格出发,返回到达最后一行任意单元格的最小路径代价。

示例 1: alt

输入:grid = [[5,3],[4,0],[2,1]], moveCost = [[9,8],[1,5],[10,12],[18,6],[2,4],[14,3]]

输出:17

解释:最小代价的路径是 5 -> 0 -> 1 。
- 路径途经单元格值之和 5 + 0 + 1 = 6 。
- 从 5 移动到 0 的代价为 3 。
- 从 0 移动到 1 的代价为 8 。
路径总代价为 6 + 3 + 8 = 17 。

示例 2:

输入:grid = [[5,1,2],[4,0,3]], moveCost = [[12,10,15],[20,23,8],[21,7,1],[8,1,13],[9,10,25],[5,3,2]]

输出:6

解释:
最小代价的路径是 2 -> 3 。 
- 路径途经单元格值之和 2 + 3 = 5 。 
- 从 2 移动到 3 的代价为 1 。 
路径总代价为 5 + 1 = 6 。

提示:

  • grid 由从 0m * n - 1 的不同整数组成

建新图 + 建虚拟点 + 堆优化 Dijkstra

注意:可以直接使用解法二的方法,但先认真看完本做法,再去看解法二,会有相当丝滑的体验。

每次移动,「实际路径权值 = 经过边的权值 + 目的地的权值」

利用原图,构建新图:「每个单元格视为一个点,除最后一行外,每个点对下一行的所有点连一条有向边,边权 = 原图中该边的权值 + 原图中该目的地的权值」

分析新图中的点边数量:

  • 点:共 个点,数量为
  • 边:不算最后一行,共 个点,这些点与下一行的每个点均有一条有向边,合计 条边,数量为

原问题转换为:求点 的最短路,其中点 所在位置为第 行,点 所在位置为第 行。

这似乎是一个「多源汇最短路」问题?但求解多源汇最短路的 Floyd 算法是 的,会超时。

实际上,我们也并不真的关心图中任意点之间的最短路,仅仅关心第一行到最后一行的最短路。

因此,「我们可通过建立“虚拟源点”和“虚拟汇点”的方式,来将“多源汇最短路”问题转换为“单源最短路”问题。」

具体的,我们创建一个“虚拟源点”,该点向所有第一行的点连权值为 的有向边;同时创建一个“虚拟汇点”,最后一行的所有点向该点连权值为 的有向边。

问题进一步转化为:求“虚拟源点”到“虚拟汇点”的最短路。

至此,我们通过 「建新图 -> 创建虚拟源汇点(转换为单源最短路)-> 套用单源最短路算法」 解决本题。

将新图中点的数量记为 ,边数记为 ,朴素 Dijkstra 复杂度为 ,堆优化的 Dijkstra 的复杂度为 ,当 (相对稀疏)时,优先使用堆优化 Dijkstra

Java 代码:

class Solution {
    int N = 50 * 50 + 2, M = 50 * 50 * 50, idx = 0, n;
    int[] he = new int[N], e = new int[M], ne = new int[M], w = new int[M];
    void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        w[idx] = c;
        he[a] = idx++;
    }
    public int minPathCost(int[][] grid, int[][] moveCost) {
        int N = grid.length, M = grid[0].length;
        int S = N * M, T = S + 1;
        n = N * M + 2;
        Arrays.fill(he, -1);
        //「虚拟源点」向「第一行」进行连边
        for (int i = 0; i < M; i++) add(S, grid[0][i], grid[0][i]);
        // 转换原图
        for (int i = 0; i < N - 1; i++) {
            for (int j = 0; j < M; j++) {
                int a = grid[i][j];
                for (int k = 0; k < M; k++) {
                    int b = grid[i + 1][k];
                    add(a, b, moveCost[a][k] + b);
                }
            }
        }
        //「最后一行」向「虚拟汇点」进行连边
        for (int i = 0; i < M; i++) add(grid[N - 1][i], T, 0);
        // 最短路
        int[] dist = dijkstra(S);
        return dist[T];
    }
    int[] dijkstra(int x) {
        // 起始先将所有的点标记为「未更新」和「距离为正无穷」
        int[] dist = new int[n];
        Arrays.fill(dist, 0x3f3f3f3f);
        boolean[] vis = new boolean[n];
        dist[x] = 0;
        // 使用「优先队列」存储所有可用于更新的点
        // 以 (点编号, 到起点的距离) 进行存储,优先弹出「最短距离」较小的点
        PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->a[1]-b[1]);
        q.add(new int[]{x, 0});
        while (!q.isEmpty()) {
            // 每次从「优先队列」中弹出
            int[] poll = q.poll();
            int u = poll[0], step = poll[1];
            // 如果弹出的点被标记「已更新」,则跳过
            if (vis[u]) continue;
            // 标记该点「已更新」,并使用该点更新其他点的「最短距离」
            vis[u] = true;
            for (int i = he[u]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dist[j] <= dist[u] + w[i]) continue;
                dist[j] = dist[u] + w[i];
                q.add(new int[]{j, dist[j]});
            }
        }
        return dist;
    }
}

C++ 代码:

class Solution {
public:
    static const int N = 50 * 50 + 2, M = 50 * 50 * 50;
    int he[N], e[M], ne[M], w[M], idx, n, INF = 0x3f3f3f3f;
    void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        w[idx] = c;
        he[a] = idx++;
    }
    int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
        int N = grid.size(), M = grid[0].size();
        int S = N * M, T = S + 1;
        n = N * M + 2;
        fill(he, he + n, -1);
        //「虚拟源点」向「第一行」进行连边
        for (int i = 0; i < M; i++) add(S, grid[0][i], grid[0][i]);
        // 转换原图
        for (int i = 0; i < N - 1; i++) {
            for (int j = 0; j < M; j++) {
                int a = grid[i][j];
                for (int k = 0; k < M; k++) {
                    int b = grid[i + 1][k];
                    add(a, b, moveCost[a][k] + b);
                }
            }
        }
        //「最后一行」向「虚拟汇点」进行连边
        for (int i = 0; i < M; i++) add(grid[N - 1][i], T, 0);
        // 最短路
        vector<int> dist = dijkstra(S);
        return dist[T];
    }
    vector<intdijkstra(int x) {
        vector<intdist(n, 0x3f3f3f3f);
        vector<boolvis(n, false);
        dist[x] = 0;
        // 使用「优先队列」存储所有可用于更新的点
        // 以 (到起点的距离, 点编号) 进行存储,优先弹出「最短距离」较小的点
        priority_queue<pair<intint>, vector<pair<intint>>, greater<pair<intint>>> q;
        q.push({0, x});
        while (!q.empty()) {
            // 每次从「优先队列」中弹出
            auto [step, u] = q.top();
            q.pop();
            // 如果弹出的点被标记「已更新」,则跳过
            if (vis[u]) continue;
            // 标记该点「已更新」,并使用该点更新其他点的「最短距离」
            vis[u] = true;
            for (int i = he[u]; i != -1; i = ne[i]) {
                int j = e[i];
                if (dist[j] <= dist[u] + w[i]) continue;
                dist[j] = dist[u] + w[i];
                q.push({dist[j], j});
            }
        }
        return dist;
    }
};

Python 代码:

import heapq

class Solution:
    def minPathCost(self, grid, moveCost):
        N, M = len(grid), len(grid[0])
        S, T = N * M, N * M + 1
        n = N * M + 2
        he = [-1] * n
        e, ne, w = [-1] * (50 * 50 * 50), [-1] * (50 * 50 * 50), [-1] * (50 * 50 * 50)
        idx = 0

        def add(a, b, c):
            nonlocal idx
            e[idx] = b
            ne[idx] = he[a]
            w[idx] = c
            he[a] = idx
            idx += 1

        def dijkstra(x):
            dist = [float('inf')] * n
            vis = [False] * n
            dist[x] = 0
            # 使用「优先队列」存储所有可用于更新的点
            # 以 (到起点的距离, 点编号) 进行存储,优先弹出「最短距离」较小的点
            q = [(0, x)]
            heapq.heapify(q)
            while q:
                # 每次从「优先队列」中弹出
                step, u = heapq.heappop(q)
                # 如果弹出的点被标记「已更新」,则跳过
                if vis[u]: continue
                # 标记该点「已更新」,并使用该点更新其他点的「最短距离」
                vis[u] = True
                i = he[u]
                while i != -1:
                    j, c = e[i], w[i]
                    i = ne[i]
                    if dist[j] <= dist[u] + c: continue
                    dist[j] = dist[u] + c
                    heapq.heappush(q, (dist[j], j))
            return dist

        #「虚拟源点」向「第一行」进行连边
        for i in range(M):
            add(S, grid[0][i], grid[0][i])
        # 转换原图
        for i in range(N - 1):
            for j in range(M):
                a = grid[i][j]
                for k in range(M):
                    b = grid[i + 1][k]
                    add(a, b, moveCost[a][k] + b)
        #「最后一行」向「虚拟汇点」进行连边
        for i in range(M):
            add(grid[N - 1][i], T, 0)
        # 最短路
        dist = dijkstra(S)
        return dist[T]
  • 时间复杂度: ,其中 为新图中的点数 为新图中的边数
  • 空间复杂度:

堆优化 Dijkstra

什么?你说你实在不想建新图,也不想搞什么虚拟点,就想用你心爱的 BFS 来做?!

我懂你意思,但那不叫 BFS

只是将「建新图」和「建虚拟点」的过程省掉,仍需要使用优先队列(堆)来每次取出当前“路径代价最小”的点来进行扩充,执行过程仍为堆优化 Dijkstra 的核心操作。

尤其所谓“省掉” 建新图 和 建虚拟点,真就字面上的“省掉”,并非不存在,因为两种做法思路是完全一致的。可简单列举「本解法」与「解法一」的对应关系:

  • 起始往队列放入首行元素,对应了解法一的“建立虚拟源点”过程;
  • 从队列中取元素出来扩充时,若当前元素所在行是最后一行时,用当前路径代价来更新答案,对应了解法一的“建立虚拟汇点”过程;
  • 扩充时直接遍历列(即下一行的所有点),对应的解法一的“用原图边建新图”的过程。

Java 代码:

class Solution {
    public int minPathCost(int[][] grid, int[][] moveCost) {
        int m = grid.length, n = grid[0].length, INF = 0x3f3f3f3f, ans = INF;
        int[][] dist = new int[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) dist[i][j] = INF;
        }
        PriorityQueue<int[]> d = new PriorityQueue<>((a,b)->a[2]-b[2]);
        for (int i = 0; i < n; i++) {
            d.add(new int[]{0, i, grid[0][i]});
            dist[0][i] = grid[0][i];
        }
        while (!d.isEmpty()) {
            int[] info = d.poll();
            int x = info[0], y = info[1], cur = info[2];
            if (x == m - 1) {
                ans = Math.min(ans, cur);
                continue;
            }
            for (int i = 0; i < n; i++) {
                int step = moveCost[grid[x][y]][i], ne = grid[x + 1][i];
                int tot = cur + step + ne;
                if (tot >= ans || dist[x + 1][i] <= tot) continue;
                dist[x + 1][i] = tot;
                d.add(new int[]{x + 1, i, tot});
            }
        }
        return ans;
    }
}

C++ 代码:

class Solution {
public:
    int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
        int m = grid.size(), n = grid[0].size(), INF = 0x3f3f3f3f, ans = INF;
        vector<vector<int>> dist(m, vector<int>(n, INF));
        priority_queue<vector<int>, vector<vector<int>>, greater<vector<int>>> pq;
        for (int i = 0; i < n; i++) {
            pq.push({0, i, grid[0][i]});
            dist[0][i] = grid[0][i];
        }
        while (!pq.empty()) {
            vector<int> info = pq.top();
            pq.pop();
            int x = info[0], y = info[1], cur = info[2];
            if (x == m - 1) {
                ans = min(ans, cur);
                continue;
            }
            for (int i = 0; i < n; i++) {
                int step = moveCost[grid[x][y]][i], ne = grid[x + 1][i];
                int tot = cur + step + ne;
                if (tot >= ans || dist[x + 1][i] <= tot) continue;
                dist[x + 1][i] = tot;
                pq.push({x + 1, i, tot});
            }
        }
        return ans;
    }
};

Python 代码:

class Solution:
    def minPathCost(self, grid, moveCost):
        m, n, INF = len(grid), len(grid[0]), float('inf')
        ans = INF
        dist = [[INF] * n for _ in range(m)]
        for i in range(n):
            dist[0][i] = grid[0][i]
        pq = [(0, i, grid[0][i]) for i in range(n)]
        while pq:
            x, y, cur = heapq.heappop(pq)
            if x == m - 1:
                ans = min(ans, cur)
                continue
            for i in range(n):
                step, ne = moveCost[grid[x][y]][i], grid[x + 1][i]
                tot = cur + step + ne
                if tot >= ans or dist[x + 1][i] <= tot: continue
                dist[x + 1][i] = tot
                heapq.heappush(pq, (x + 1, i, tot))
        return ans
  • 时间复杂度: ,其中 为新图中的点数 为新图中的边数
  • 空间复杂度:

原地模拟

什么?你说你连图论的方法都不想用,想就着题意做一遍?

可以。甚至当你调整更新方向,还能利用已有的 grid,实现原地模拟。

具体的,我们将“从上往下走”调整为“从下往上走”,这样可以确保当我们使用底下一行 来更新当前行 时,所用到的 不会被覆盖。

Java 代码:

class Solution {
    public int minPathCost(int[][] grid, int[][] moveCost) {
        int m = grid.length, n = grid[0].length, INF = 0x3f3f3f3f, ans = INF;
        for (int i = m - 2; i >= 0; i--) {
            for (int j = 0; j < n; j++) {
                int cur = INF;
                for (int k = 0; k < n; k++) cur = Math.min(cur, grid[i + 1][k] + moveCost[grid[i][j]][k]);
                grid[i][j] += cur;
            }
        }
        for (int i = 0; i < n; i++) ans = Math.min(ans, grid[0][i]);
        return ans;
    }
}

C++ 代码:

class Solution {
public:
    int minPathCost(vector<vector<int>>& grid, vector<vector<int>>& moveCost) {
        int m = grid.size(), n = grid[0].size(), INF = INT_MAX, ans = INF;
        for (int i = m - 2; i >= 0; i--) {
            for (int j = 0; j < n; j++) {
                int cur = INF;
                for (int k = 0; k < n; k++) cur = min(cur, grid[i + 1][k] + moveCost[grid[i][j]][k]);
                grid[i][j] += cur;
            }
        }
        for (int i = 0; i < n; i++) ans = min(ans, grid[0][i]);
        return ans;
    }
};

Python 代码:

class Solution:
    def minPathCost(self, grid, moveCost):
        m, n = len(grid), len(grid[0])
        for i in range(m - 2-1-1):
            for j in range(n):
                grid[i][j] += min([grid[i + 1][k] + moveCost[grid[i][j]][k] for k in range(n)])
        return min([grid[0][i] for i in range(n)])

TypeScript 代码:

function minPathCost(grid: number[][], moveCost: number[][]): number {
    let m = grid.length, n = grid[0].length, INF = 0x3f3f3f3f, ans = INF;
    for (let i = m - 2; i >= 0; i--) {
        for (let j = 0; j < n; j++) {
            let cur = INF;
            for (let k = 0; k < n; k++) cur = Math.min(cur, grid[i + 1][k] + moveCost[grid[i][j]][k]);
            grid[i][j] += cur;
        }
    }
    for (let i = 0; i < n; i++) ans = Math.min(ans, grid[0][i]);
    return ans;
};
  • 时间复杂度: ,其中 分别代表给定 grid 的长宽
  • 空间复杂度:

最后

这是我们「刷穿 LeetCode」系列文章的第 No.2304 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

相关文章:

2304. 网格中的最小路径代价 : 从「图论最短路」过渡到「O(1) 空间的原地模拟」

题目描述 这是 LeetCode 上的 「2304. 网格中的最小路径代价」 &#xff0c;难度为 「中等」。 Tag : 「最短路」、「图」、「模拟」、「序列 DP」、「动态规划」 给你一个下标从 0 开始的整数矩阵 grid&#xff0c;矩阵大小为 m x n&#xff0c;由从 0 到 的不同整数组成。 你…...

【机器学习】算法性能评估常用指标总结

考虑一个二分问题&#xff0c;即将实例分成正类&#xff08;positive&#xff09;或负类&#xff08;negative&#xff09;。对一个二分问题来说&#xff0c;会出现四种情况。如果一个实例是正类并且也被 预测成正类&#xff0c;即为真正类&#xff08;True positive&#xff0…...

前端 JavaScript 与 HTML 怎么实现交互?

前端的交互性是通过JavaScript与HTML结合实现的。JavaScript作为一种脚本语言&#xff0c;可以嵌入HTML中&#xff0c;通过对DOM&#xff08;文档对象模型&#xff09;的操作&#xff0c;实现与用户的交互。以下将详细介绍前端JavaScript与HTML如何实现交互&#xff0c;包括事件…...

命令执行总结

之前做了一大堆的题目 都没有进行总结 现在来总结一下命令执行 我遇到的内容 这里我打算按照过滤进行总结 依据我做过的题目 过滤system 下面是一些常见的命令执行内容 system() passthru() exec() shell_exec() popen() proc_open() pcntl_exec() 反引号 同shell_exec() …...

机器学习——词向量模型(CBOW代码实现-未开始)

本来是不打算做这个CBOW代码案例的&#xff0c;想快马加鞭看看前馈神经网络 毕竟书都买好了 可是…可是…我看书的时候&#xff0c;感觉有点儿困难&#xff0c;哭的很大声… 感觉自己脑细胞可能无法这么快接受 要不&#xff0c;还是退而求个稍微难度没那么大的事&#xff0c;想…...

智慧海岛/海域方案:助力海洋空间智慧化、可视化管理

随着我国海洋经济的快速发展&#xff0c;海域海岛的安防技术也获得了进步。传统的安防监控模式已经满足不了海域海岛的远程监管需求。伴随着人工智能、边缘计算、大数据、通信传输技术、视频技术、物联网等信息化技术的发展&#xff0c;海岛海域在监管手段上&#xff0c;也迎来…...

Bin、Hex、ELF、AXF的区别

1.Bin Bin文件是最纯粹的二进制机器代码, 或者说是"顺序格式"。按照assembly code顺序翻译成binary machine code&#xff0c;内部没有地址标记。Bin是直接的内存映象表示&#xff0c;二进制文件大小即为文件所包含的数据的实际大小。 BIN文件就是直接的二进制文件&…...

IDEA安装教程

文章目录 1 下载IntelliJ IDEA2 安装3 IDEA配置4 创建项目 1 下载IntelliJ IDEA ​ 官方网站上下载最新版本的IntelliJ IDEA。官方网站提供了两个版本&#xff1a;Community版和Ultimate版。 Community版是免费的&#xff0c;适用于个人和非商业用途。Ultimate版则需要付费购…...

DRF-项目-(1):构建纯净版的drf项目,不再使用django的后台管理,django的认证,django的session等功能,作为一个纯接口项目

项目的目录结构&#xff1a; -HeartFailure |-- apps |--user |--HeartFailure |-- static |--manage.py 一、django项目相关的 1、命令行中创建django项目 #1、切换到指定的虚拟环境中 workon my_drf#2、该虚拟环境已经安装好django和rest_framework了 django-admin startp…...

ubuntu 手动清理内存cache

/proc是一个虚拟文件系统&#xff0c;我们可以通过对它的读写操作来做为与kernel实体间进行通信的一种手段。也就是说可以通过修改/proc中的文件&#xff0c;来对当前kernel的行为做出调整。 那么我们可以通过调整/proc/sys/vm/drop_caches来释放内存。操作如下&#xff1a; …...

gitBash中如何使用Linux中的tree命令

文章目录 在gitBash中安装tree的目的如何安装安装完成,就可以直接完美适配Linux系统了 在gitBash中安装tree的目的 如下图,powershell虽然可以看做是window下的Linux系统,但是根本就不适配很多Linux中的命令 如何安装 tree.exe安装网址 下载 tree 命令的 二进制包&#xf…...

【鸿蒙应用ArkTS开发系列】- 灌水区,鸿蒙ArkTs开发有问题可以在该帖中反馈

大家好, 这是一篇水贴&#xff0c;给大家提供一个交流沟通鸿蒙开发遇到问题的地方。 新增新增这个文章呢&#xff0c;大家在开发使用ArkTS开发鸿蒙应用或者鸿蒙服务的时候&#xff0c;有遇到疑问或者问题&#xff0c;可以在本文章评论区提问&#xff0c;我看到了如果知道怎么…...

c语言习题1124

分别定义函数求圆的面积和周长。 写一个函数&#xff0c;分别求三个数当中的最大数。 写一个函数&#xff0c;计算输入n个数的乘积 一个判断素数的函数&#xff0c;在主函数输入一个整数&#xff0c;输出是否为素数的信息 写一个函数求n! ,利用该函数求1&#xff01;2&…...

线段树---数据结构学习

线段树的教程可以参照线段树 这里推荐 https://oi-wiki.org/ 这个网站&#xff0c;数据结构讲的非常透。 线段树学了很多次忘了很多次&#xff0c;这次打算记录一下以后方便回顾(leetcode这类题遇见的不算特别多)。 样板例题 leltcode-307 #题目样板 class NumArray {private …...

linux基础5:linux进程1(冯诺依曼体系结构+os管理+进程状态1)

冯诺依曼体系结构os管理 一.冯诺依曼体系结构&#xff1a;1.简单介绍&#xff08;准备一&#xff09;2.场景&#xff1a;1.程序的运行&#xff1a;2.登录qq发送消息&#xff1a; 3.为什么需要内存&#xff1a;1.简单的引入&#xff1a;2.计算机存储体系&#xff1a;3.内存的意义…...

JVM-基础

jdk7及以前&#xff1a; 通过-XX:PermSize 来设置永久代初始分配空间&#xff0c;默认值是20.75m -XX:MaxPermSize来设定永久代最大可分配空间&#xff0c;32位是64m&#xff0c;64位是82m jdk8及之后&#xff1a; 通过-XX:MetaspaceSize 来设置永久代初始分配空间&#xff…...

Baidu Comate 基于百度文心一言的智能编码助手

本心、输入输出、结果 文章目录 Baidu Comate 基于百度文心一言的智能编码助手前言产品能力主要功能特性JetBrains IntelliJ IDEA 插件安装相关链接花有重开日,人无再少年实践是检验真理的唯一标准Baidu Comate 基于百度文心一言的智能编码助手 编辑:简简单单 Online zuozuo …...

基本微信小程序的图书馆座位管理系统

项目介绍 图书馆因有良好的学习氛围、大量的学习资源吸引大家前来学习,图书馆还未开馆就有大量的用户在门口排队等待,有限的座位与日益增加的自主学习者之间形成了供不应求的现象,再加上不了解图书馆的座位使用情况和恶意占座等现象,使得有限的学习座位越发紧张。本团队针对此…...

2023年亚太杯数学建模A题水果采摘机器人的图像识别功能(免费思路)

中国是世界上最大的苹果生产国&#xff0c;年产量约为 3500 万吨。同时&#xff0c;中国也是世界上最大的苹果出口国&#xff0c;世界上每两个苹果中就有一个出口到国。世界上每两个苹果中就有一个来自中国&#xff0c;中国出口的苹果占全球出口量的六分之一以上。来自中国。中…...

AWS CLI和EKSCTL的客户端设置

文章目录 小结过程安装AWS CLI安装EKSCTL在两个Kubernetes Cluster之间切换 参考 小结 在Linux环境中对AWS CLI和EKSCTL的客户端进行了设置。 过程 安装AWS CLI 使用以下指令安装&#xff1a; curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容

基于 ​UniApp + WebSocket​实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配​微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...