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

news2024/9/23 23:34:32

题目描述

这是 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 原题链接和其他优选题解。

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

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

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

相关文章

Java线程的学习

本来我以为这可能只是Java里的一小块知识点&#xff0c;但当我搜索自己关注的Up主的网课时&#xff0c;觉得还是开一个系列来记录好了。我的记录绝不仅仅是照搬课程中的内容&#xff0c;我会带上自己的理解以及示例代码、并且是按照本人的专业课老师上课的节奏来记录&#xff0…

maven打包可执行jar含依赖lib

修改pom.xml <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><!-- jdk8可用&#xff0c;其他jdk版本可能需改插件版本 --><version>2.3.7.RE…

迁新址 启新程|美创科技杭州总部乔迁仪式圆满举行

“迁新址 启新程” 2023年11月21日 美创科技杭州总部乔迁仪式隆重举行 杭州未来科技城管委会、余杭国投集团、浙江省网络空间安全协会、浙江鸿程、华睿投资、金艮投资、如山资本、赛伯乐投资、宽带资本、普华投资、国中创投、密码资本、东方富海、之江商学、阿里云、联通&…

AI辅助带货直播场景源码系统 附带网站的搭建教程

互联网技术的发展和普及&#xff0c;直播带货行业迅速崛起。然而&#xff0c;直播带货在带来商机的同时&#xff0c;也面临着诸多挑战。如直播内容缺乏新意、转化率低等问题。针对这些问题&#xff0c;AI辅助带货直播场景源码系统应运而生&#xff0c;旨在利用人工智能技术&…

项目管理PMP6.0-五大过程组、十大知识领域、四十九个过程(记忆码:7664363734)

项目管理PMP6.0-五大过程组、十大知识领域、四十九个过程&#xff08;记忆码&#xff1a;7664363734&#xff09; 项目经理的影响力范围三者关系图&#xff08;五大过程组、十大知识领域、四十九个过程&#xff09;五大过程组十大知识领域十大知识领域之间联系 四十九个过程&am…

C# Onnx 特征匹配 DeDoDe 检测,不描述---描述,不检测

目录 介绍 效果 模型信息 项目 代码 下载 介绍 github地址&#xff1a;https://github.com/Parskatt/DeDoDe DeDoDe &#x1f3b6; Detect, Dont Describe - Describe, Dont Detect, for Local Feature Matching The DeDoDe detector learns to detect 3D consisten…

FSCTF2023-Reverse方向题解WP。学习贴

文章目录 [FSCTF 2023]signin[FSCTF 2023]MINE SWEEPER[FSCTF 2023]Xor[FSCTF 2023]EZRC4[FSCTF 2023]ez_pycxor[FSCTF 2023]Tea_apk[FSCTF 2023]ezcode[FSCTF 2023]ezbroke[FSCTF 2023]rrrrust!!![FSCTF2023]ezrev&#xff08;未解决&#xff09; [FSCTF 2023]signin UPX壳&am…

arcgis中投影文件(.prj)和地理转换文件(.gtf)存储路径

1、投影文件&#xff08;自定义的.prj&#xff09;的存储路径 C:\Users\14635\AppData\Roaming\ESRI\Desktop10.5\ArcMap\Coordinate Systems 2、地理转换文件&#xff08;.gtf&#xff09;--自定义 C:\Users\14635\AppData\Roaming\ESRI\Desktop10.5\ArcToolbox\CustomTransfo…

ROS2串口通讯serial库(适用于humble版本)

要的串口操作的API介绍在这里&#xff1a;serial: serial::Serial Class Reference (wjwwood.io) 但是我们不是直接利用上面这个东西&#xff0c;而是使用的是根据这个改写的一个针对ros2的一个serial库&#xff0c;这个serial库是根据上面这个库改写来的&#xff0c;ros2的库在…

分布式系统的认证授权

一.分布式系统的认证授权大致架构 以云音乐系统为例&#xff1a; 注&#xff1a;一般情况下&#xff0c;我们会把认证的部分的接口提取为一个单独的认证服务模块中。 二.单点登录&#xff08;Single Sign On&#xff09; 单点登录&#xff0c;Single Sign On&#xff0c;简称…

140.【鸿蒙OS开发-01】

鸿蒙开发 (一)、初识鸿蒙1.初识鸿蒙(1).移动通讯技术的发展(2).完整的鸿蒙开发 (二)、鸿蒙系统介绍1.鸿蒙系统的官方定义(1).鸿蒙操作系统概述(2).鸿蒙的生态 2.鸿蒙系统的特点3.鸿蒙和安卓的对比4.鸿蒙开发的发展前景 (三)、鸿蒙开发准备工作1.鸿蒙OS的完整开发流程2.注册并实…

Win10系统无法登录Xbox live的四种解决方法

在Win10系统中&#xff0c;用户可以登录Xbox live平台&#xff0c;畅玩自己喜欢的游戏。但是&#xff0c;有用户却遇到了无法登录Xbox live的问题。接下来小编给大家详细介绍四种简单的解决方法&#xff0c;解决后用户在Win10电脑上就能成功登录上Xbox live平台。 Win10系统无法…

MySql 计算同比、环比

一、理论 国家统计局同比、环比计算公式 增长速度是反映经济社会某一领域发展变化情况的重要数据&#xff0c;而同比和环比是反映增长速度最基础、最核心的数据指标&#xff0c;也是国际上通用的指标。在统计中&#xff0c; 同比和环比通常是同比变化率和环比变化率的简称&…

二百零四、Flume——登录监听窗口报错Ncat: bind to :::44444: Address already in use. QUITTING.

一、目的 Flume安装好后测试开启监听窗口44444&#xff0c;结果报错Ncat: bind to :::44444: Address already in use. QUITTING. 二、报错详情 Ncat: bind to :::44444: Address already in use. QUITTING. 三、报错原因 经过分析发现&#xff0c;44444窗口已经被占用 […

sqli-labs(3)

11. 看到登录框直接or 11 在hackerabar中我们可以看到这里是post传递的数据&#xff0c;在get中用--来注释后面的内容 因为get中#是用来指导浏览器动作的&#xff0c;--代表注释是空格&#xff0c;所以这里用# 之后就和get的一样了 1 order by 2 # order by 3报错 联合注入 …

【5k字长文 | Vue学习笔记】#1 认识Vue对象和基础语法

Vue是一个非常流行的渐进式JavaScript框架&#xff0c;渐进式指的是自底向上&#xff0c;从小组件逐渐向上构成整个项目&#xff0c;渐进式还可以理解为&#xff1a;用什么就拿什么&#xff0c;每个组件只做自己的事&#xff0c;尽可能解耦合。 本节我们将学习简单的Vue实例&a…

渗透测试高级技巧(一):分析验签与前端加密

“开局一个登录框” 在黑盒的安全测试的工作开始的时候&#xff0c;打开网站一般来说可能仅仅是一个登录框&#xff1b;很多时候这种系统往往都是自研或者一些业务公司专门研发。最基础的情况下&#xff0c;我们会尝试使用 SQL 注入绕过或者爆破之类的常规手段&#xff0c;如果…

【文末送书】十大排序算法C++代码实现

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

数独·12中解法·anroid 数独小游戏·休闲益智小游戏

标题数独12中解法anroid 数独小游戏休闲益智小游戏&#xff08;继续更新中……&#xff09; 一款经典数独训练app 资源下载 &#xff08;0积分&#xff09;https://download.csdn.net/download/qq_38355313/88544810 —— —— 数独&#xff08;sh d&#xff09;是源自18世纪…

Find My音箱|苹果Find My技术与音箱结合,智能防丢,全球定位

音箱市场规模正在不断扩大。随着人们生活品质的提高&#xff0c;对音乐体验的需求也在不断升级。消费者对于蓝牙音箱的需求&#xff0c;已经从单纯的音质扩展到了功能、设计和价格等多个方面。随着移动化、即时化的视听娱乐需求的增长&#xff0c;蓝牙音箱性能、质量、外观设计…