算法【Dijkstra算法及分层图最短路】

news2024/9/20 23:35:17

Dijkstra算法:给定一个源点,求解从源点到每个点的最短路径长度。单源最短路径算法。

适用范围:有向图、边的权值没有负数。

彻底暴力的Dijkstra算法,不讲、时间复杂度太差、无意义。

普通堆实现的Dijkstra算法,最普遍、最常用。

算法核心过程:

节点弹出过就忽略,节点没弹出过,让其它没弹出节点距离变小的记录加入堆。

反向索引堆实现的Dijkstra算法,最快速、最极致。

普通堆实现的Dijkstra算法,时间复杂度O(m * log m),m为边数。

1.distance[i]表示从源点到i点的最短距离,visited[i]表示i节点是否从小根堆弹出过。

2.准备好小根堆,小根堆存放记录:(x点,源点到x的距离),小根堆根据距离组织。

3.令distance[源点]=0,(源点,0)进入小根堆。

4.从小根堆弹出(u点,源点到u的距离)

   a. 如果visited[u] == true,不做任何处理,重复步骤4。

   b. 如果visited[u] == false,令visited[u] = true,u就算弹出过了

      然后考察u的每一条边,假设某边去往v,边权为w。

      1)如果visited[v] == false 并且 distance[u] + w < distance[v]

         令distance[v] = distance[u] + w,把(v, distance[u] + w)加入小根堆。

      2)处理完u的每一条边之后,重复步骤4。

5.小根堆为空过程结束,distance表记录了源点到每个节点的最短距离。

反向索引堆实现的Dijkstra算法,时间复杂度O(m * log n),n为节点数,m为边数。

1.准备好反向索引堆,根据源点到当前点的距离组织小根堆,可以做到如下操作

   a. 新增记录(x, 源点到x的距离)   b. 当源点到x的距离更新时,可以进行堆的调整

   c. x点一旦弹出,以后忽略x       d. 弹出堆顶的记录(u, 源点到u的距离)

2.把(源点,0)加入反向索引堆,过程开始。

3.反向索引堆弹出(u,源点到u的距离),考察u的每一条边,假设某边去往v,边权为w

   1)如果v没有进入过反向索引堆里,新增记录(v, 源点到u的距离 + w)。

   2)如果v曾经从反向索引堆弹出过,忽略。

   3)如果v在反向索引堆里,看看源点到v的距离能不能变得更小,如果能,调整堆;不能,忽略。

   4)处理完u的每一条边,重复步骤3。

4.反向索引堆为空过程结束。反向索引堆里记录了源点到每个节点的最短距离。

分层图最短路,又叫扩点最短路。

不把实际位置看做图上的点,而是把 实际位置及其状态的组合看做是图上的点,然后搜索bfs或者Dijkstra的过程不变,只是扩了点(分层)而已。原理简单,核心在于如何扩点、如何到达、如何算距离,每个题可能都不一样。

下面通过几个题目加深理解。

题目一

测试链接:https://leetcode.cn/problems/network-delay-time

分析:这个题就是一个Dijkstra算法模板。找到最晚时间就是结果。下面分别给出使用堆,也就是优先队列的做法以及使用反向索引堆的做法。代码如下。

class Solution {
public:
    int Head[101] = {0};
    int Next[6001] = {0};
    int To[6001] = {0};
    int Weigth[6001] = {0};
    int cnt = 1;
    int distance[101];
    bool poped[101] = {false};
    void build(vector<vector<int>>& times, int n){
        int length = times.size();
        for(int i = 0;i < length;++i){
            Next[cnt] = Head[times[i][0]];
            Head[times[i][0]] = cnt;
            To[cnt] = times[i][1];
            Weigth[cnt] = times[i][2];
            ++cnt;
        }
        for(int i = 1;i <= n;++i){
            distance[i] = -((1 << 31) + 1);
        }
    }
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        build(times, n);
        int ans = 0;
        int num = n;
        auto cmp = [](vector<int> v1, vector<int> v2)->bool{
            return v1[1] > v2[1];
        };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);
        vector<int> temp;
        temp.push_back(k);
        temp.push_back(0);
        distance[k] = 0;
        --num;
        q.push(temp);
        temp.clear();
        while (!q.empty())
        {
            temp = q.top();
            q.pop();
            if(poped[temp[0]] == true){
                continue;
            }
            poped[temp[0]] = true;
            ans = ans > distance[temp[0]] ? ans : distance[temp[0]];
            for(int next = Head[temp[0]];next != 0;next = Next[next]){
                if(Weigth[next] + temp[1] < distance[To[next]]){
                    if(distance[To[next]] == -((1 << 31) + 1)){
                        --num;
                    }
                    vector<int> tmp;
                    tmp.push_back(To[next]);
                    tmp.push_back(Weigth[next] + temp[1]);
                    q.push(tmp);
                    distance[To[next]] = Weigth[next] + temp[1];
                }
            }
        }
        return num == 0 ? ans : -1;
    }
};

其中,采用链式前向星建图;具体过程和上面写的Dijkstra算法流程一样。下面是采用反向索引堆的做法。代码如下。

class Solution {
public:
    int Head[101] = {0};
    int Next[6001] = {0};
    int To[6001] = {0};
    int Weight[6001] = {0};
    int cnt = 1;
    int heap[101];
    int where[101];
    int distance[101];
    int heapSize = 0;
    void Build(vector<vector<int>>& times, int n){
        int length = times.size();
        for(int i = 0;i < length;++i){
            Next[cnt] = Head[times[i][0]];
            Head[times[i][0]] = cnt;
            To[cnt] = times[i][1];
            Weight[cnt] = times[i][2];
            ++cnt;
        }
        for(int i = 1;i <= n;++i){
            distance[i] = -((1 << 31) + 1);
            where[i] = -1;
        }
    }
    void heapInsert(int i){
        int father = (i-1) / 2;
        while (distance[heap[i]] < distance[heap[father]])
        {
            swap(i, father);
            i = father;
            father = (i-1) / 2;
        }
    }
    void heapify(int i){
        int l = 2 * i + 1;
        while (l < heapSize)
        {
            int best = l+1 < heapSize && distance[heap[l]] > distance[heap[l+1]] ? l+1 : l;
            best = distance[heap[i]] < distance[heap[best]] ? i : best;
            if(best == i){
                break;
            }
            swap(i, best);
            i = best;
            l = 2 * i + 1;
        }
    }
    void swap(int a, int b){
        int temp = heap[a];
        heap[a] = heap[b];
        heap[b] = temp;
        where[heap[a]] = a;
        where[heap[b]] = b;
    }
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
        int num = n;
        int ans = 0;
        Build(times, n);
        distance[k] = 0;
        --num;
        heap[0] = k;
        where[k] = 0;
        ++heapSize;
        while (heapSize != 0)
        {
            int cur = heap[0];
            int cur_distance = distance[cur];
            swap(0, heapSize-1);
            where[cur] = -2;
            ans = ans > cur_distance ? ans : cur_distance;
            --heapSize;
            heapify(0);
            for(int next = Head[cur];next != 0;next = Next[next]){
                int to = To[next];
                int weight = Weight[next];
                if(where[to] == -1){
                    heap[heapSize] = to;
                    distance[to] = cur_distance + weight;
                    --num;
                    where[to] = heapSize;
                    ++heapSize;
                    heapInsert(heapSize-1);
                }else if(where[to] == -2){
                    continue;
                }else{
                    if(cur_distance + weight < distance[to]){
                        distance[to] = cur_distance + weight;
                        heapInsert(where[to]);
                    }
                }
            }
        }
        return num == 0 ? ans : -1;
    }
};

其中,采用链式前向星建图;并且堆使用自己定义的堆,heapInsert方法代表从i位置向上调整堆,heapify方法代表从i位置向下调整堆。

题目二

测试链接:https://leetcode.cn/problems/path-with-minimum-effort/

分析:这也是一个Dijkstra算法模板和第一题主体代码类似,只不过给出的数据就相当于图,就没在建图。代码如下。

class Solution {
public:
    int arr[5] = {0, 1, 0, -1, 0};
    int distance[100][100];
    bool poped[100][100];
    void build(int row, int column){
        for(int i = 0;i < row;++i){
            for(int j = 0;j < column;++j){
                distance[i][j] = -((1 << 31) + 1);
                poped[i][j] = false;
            }
        }
    }
    int minimumEffortPath(vector<vector<int>>& heights) {
        int row = heights.size();
        int column = heights[0].size();
        build(row, column);
        auto cmp = [](vector<int> v1, vector<int> v2){
            return v1[2] > v2[2];
        };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);
        vector<int> temp;
        temp.push_back(0);
        temp.push_back(0);
        temp.push_back(0);
        distance[0][0] = 0;
        q.push(temp);
        temp.clear();
        while (!q.empty())
        {
            temp = q.top();
            q.pop();
            if(poped[temp[0]][temp[1]] == true){
                continue;
            }
            poped[temp[0]][temp[1]] = true;
            for(int index = 0;index < 4;++index){
                if(temp[0]+arr[index] >= 0 && temp[0]+arr[index] < row
                && temp[1]+arr[index+1] >= 0 && temp[1]+arr[index+1] < column){
                    int cost = temp[2] > abs(heights[temp[0]][temp[1]] - heights[temp[0]+arr[index]][temp[1]+arr[index+1]]) ?
                    temp[2] : abs(heights[temp[0]][temp[1]] - heights[temp[0]+arr[index]][temp[1]+arr[index+1]]);
                    if(cost < distance[temp[0]+arr[index]][temp[1]+arr[index+1]]){
                        vector<int> tmp;
                        tmp.push_back(temp[0]+arr[index]);
                        tmp.push_back(temp[1]+arr[index+1]);
                        tmp.push_back(cost);
                        distance[temp[0]+arr[index]][temp[1]+arr[index+1]] = cost;
                        q.push(tmp);
                    }
                }
            }
        }
        return distance[row-1][column-1];
    }
};

其中,由于图的特殊性,邻接节点为上下左右的点,所以代码也像分支限界法。

题目三

测试链接:https://leetcode.cn/problems/swim-in-rising-water/

分析:这道题和题目二思路大致一样,只不过需要判断的东西不同。代码如下。

class Solution {
public:
    int arr[5] = {0, -1, 0, 1, 0};
    int distance[50][50];
    bool poped[50][50] = {false};
    void build(int n){
        for(int i = 0;i < n;++i){
            for(int j = 0;j < n;++j){
                distance[i][j] = -((1 << 31) + 1);
            }
        }
    }
    int swimInWater(vector<vector<int>>& grid) {
        int n = grid.size();
        build(n);
        auto cmp = [](vector<int> v1, vector<int> v2){
            return v1[2] > v2[2];
        };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);
        distance[0][0] = 0;
        vector<int> temp;
        temp.push_back(0);
        temp.push_back(0);
        temp.push_back(grid[0][0]);
        q.push(temp);
        temp.clear();
        while (!q.empty())
        {
            temp = q.top();
            q.pop();
            if(temp[0] == n-1 && temp[1] == n-1){
                return temp[2];
            }
            if(poped[temp[0]][temp[1]] == true){
                continue;
            }
            poped[temp[0]][temp[1]] = true;
            for(int index = 0;index < 4;++index){
                if(temp[0]+arr[index] >= 0 && temp[0]+arr[index] < n
                && temp[1]+arr[index+1] >= 0 && temp[1]+arr[index+1] < n){
                    int cost = temp[2] > grid[temp[0]+arr[index]][temp[1]+arr[index+1]] ?
                    temp[2] : grid[temp[0]+arr[index]][temp[1]+arr[index+1]];
                    if(cost < distance[temp[0]+arr[index]][temp[1]+arr[index+1]]){
                        distance[temp[0]+arr[index]][temp[1]+arr[index+1]] = cost;
                        vector<int> tmp;
                        tmp.push_back(temp[0]+arr[index]);
                        tmp.push_back(temp[1]+arr[index+1]);
                        tmp.push_back(cost);
                        q.push(tmp);
                    }
                }
            }
        }
        return 0;
    }
};

其中,也是因为这个图中节点的邻接节点就是四周的节点,所以感觉起来也像是分支限界法。

题目四

测试链接:https://leetcode.cn/problems/shortest-path-to-get-all-keys

分析:这道题就是分层图最短路的一个题,图中的节点并不是单纯的位置,而是一个位置加上这个处于这个位置的钥匙状态,组成了一个节点。就可以看成一个位置,因为处在这个位置的具有钥匙状态的不一样,从而一个位置可以分层,从而这个图就变成了分层图。而因为图的特殊性,邻接节点就是四周的点,所以对于分层图,我们可以使用bfs展开。一旦得到钥匙状态为具有全部钥匙则返回移动次数,即可得到最小移动次数。代码如下。

class Solution
{
public:
    int k = 0;
    bool visited[30][30][64] = {false};
    int arr[5] = {0, 1, 0, -1, 0};
    void get_k(vector<string> &grid, int row, int column, int &begin_x, int &begin_y)
    {
        for (int i = 0; i < row; ++i)
        {
            for (int j = 0; j < column; ++j)
            {
                if ('a' <= grid[i][j] && grid[i][j] <= 'f')
                {
                    ++k;
                }
                if (grid[i][j] == '@')
                {
                    begin_x = i;
                    begin_y = j;
                }
            }
        }
    }
    int shortestPathAllKeys(vector<string> &grid)
    {
        int row = grid.size();
        int column = grid[0].size();
        int begin_x, begin_y;
        int step = 0;
        int cur_x, cur_y, cur_key, next_x, next_y, next_key, bit, nums;
        get_k(grid, row, column, begin_x, begin_y);
        queue<vector<int>> q;
        vector<int> first;
        first.push_back(begin_x);
        first.push_back(begin_y);
        first.push_back(0);
        q.push(first);
        visited[begin_x][begin_y][0] = true;
        while (!q.empty())
        {
            nums = q.size();
            for (int i = 0; i < nums; ++i)
            {
                cur_x = q.front()[0];
                cur_y = q.front()[1];
                cur_key = q.front()[2];
                q.pop();
                if (cur_key == (int)(pow(2, k) - 1))
                {
                    return step;
                }
                for (int index = 0; index < 4; ++index)
                {
                    next_x = cur_x + arr[index];
                    next_y = cur_y + arr[index + 1];
                    if (next_x >= 0 && next_x < row && next_y >= 0 && next_y < column && grid[next_x][next_y] != '#')
                    {
                        if (grid[next_x][next_y] == '.' || grid[next_x][next_y] == '@')
                        {
                            if (!visited[next_x][next_y][cur_key])
                            {
                                visited[next_x][next_y][cur_key] = true;
                                vector<int> temp;
                                temp.push_back(next_x);
                                temp.push_back(next_y);
                                temp.push_back(cur_key);
                                q.push(temp);
                            }
                        }
                        else if (grid[next_x][next_y] >= 'a' && grid[next_x][next_y] <= 'f')
                        {
                            bit = grid[next_x][next_y] - 'a';
                            next_key = (cur_key | (1 << bit));
                            if (!visited[next_x][next_y][next_key])
                            {
                                visited[next_x][next_y][next_key] = true;
                                vector<int> temp;
                                temp.push_back(next_x);
                                temp.push_back(next_y);
                                temp.push_back(next_key);
                                q.push(temp);
                            }
                        }
                        else if (grid[next_x][next_y] >= 'A' && grid[next_x][next_y] <= 'F')
                        {
                            bit = grid[next_x][next_y] - 'A';
                            if ((cur_key & (1 << bit)) && !visited[next_x][next_y][cur_key])
                            {
                                visited[next_x][next_y][cur_key] = true;
                                vector<int> temp;
                                temp.push_back(next_x);
                                temp.push_back(next_y);
                                temp.push_back(cur_key);
                                q.push(temp);
                            }
                        }
                    }
                }
            }
            ++step;
        }
        return -1;
    }
};

其中,对于钥匙状态采用位图的方式存储,即有a钥匙,则第0位为1,有b钥匙则第1位为1,关于位图的具体操作,可以查看之前的文章。

题目五

测试链接:https://leetcode.cn/problems/DFPeFJ/

分析:这个也是一个分层图最短路,图中节点并不是单纯的城市,而是这个城市以及处于这个城市所具有的电量。并且这个题需要我们自己建图,不像上一题有图的特殊性,所以我们采用Dijkstra流程展开。代码如下。

class Solution {
public:
    int Head[101] = {0};
    int Next[401] = {0};
    int To[401] = {0};
    int Weight[401] = {0};
    int count = 1;
    bool poped[100][101] = {false};
    void build(vector<vector<int>>& paths){
        int length = paths.size();
        for(int i = 0;i < length;++i){
            Next[count] = Head[paths[i][0]+1];
            Head[paths[i][0]+1] = count;
            To[count] = paths[i][1]+1;
            Weight[count] = paths[i][2];
            ++count;
            Next[count] = Head[paths[i][1]+1];
            Head[paths[i][1]+1] = count;
            To[count] = paths[i][0]+1;
            Weight[count] = paths[i][2];
            ++count;
        }
    }
    int electricCarPlan(vector<vector<int>>& paths, int cnt, int start, int end, vector<int>& charge) {
        cout<<end<<endl;
        build(paths);
        auto cmp = [](vector<int> v1, vector<int> v2)->bool{
            return v1[2] > v2[2];
        };
        priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);
        vector<int> first;
        int cur_city, cur_oil, cur_time, next_city, next_oil, next_time, to_city, to_oil;
        first.push_back(start);
        first.push_back(0);
        first.push_back(0);
        q.push(first);
        while (!q.empty())
        {
            cur_city = q.top()[0];
            cur_oil = q.top()[1];
            cur_time = q.top()[2];
            q.pop();
            if(cur_city == end){
                return cur_time;
            }
            if(poped[cur_city][cur_oil] == true){
                continue;
            }
            poped[cur_city][cur_oil] = true;
            if(cur_oil < cnt){
                if(poped[cur_city][cur_oil+1] == false){
                    next_city = cur_city;
                    next_oil = cur_oil + 1;
                    next_time = cur_time + charge[cur_city];
                    vector<int> temp;
                    temp.push_back(next_city);
                    temp.push_back(next_oil);
                    temp.push_back(next_time);
                    q.push(temp);
                }
            }
            for(int next = Head[cur_city+1];next != 0;next = Next[next]){
                to_city = To[next]-1;
                to_oil = Weight[next];
                if(to_oil <= cur_oil && poped[to_city][cur_oil-to_oil] == false){
                    next_oil = cur_oil - to_oil;
                    next_city = to_city;
                    next_time = cur_time + to_oil;
                    vector<int> temp;
                    temp.push_back(next_city);
                    temp.push_back(next_oil);
                    temp.push_back(next_time);
                    q.push(temp);
                }
            }
        }
        return 0;
    }
};

其中,采用链式前向星建图;对于一个节点,如果电量不满则可以选择在当前城市充电以及满足条件的话可以去到另一城市,直到第一次到达目标城市即可得到答案。

题目六

测试链接:https://www.luogu.com.cn/problem/P4568

分析:这道题和上一题比较类似,节点为一个城市以及处于这个城市还具有多少次免费搭乘飞机的次数。但是这里在扩展节点的时候,有一点小贪心。我们不能随便使用这个免费次数,只有当到达下一个城市且下一个城市的免费次数为当前免费次数减1的费用大于处于当前城市且处于当前免费次数时,我们才会使用这个免费次数。如果不使用免费次数,则代表着到达下一个城市且免费次数和当前免费次数相同的状态的费用大于处于当前城市且当前免费次数的费用加上去往下一城市的费用。分别对应代码中for循环第一个if和第二个if。代码如下。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int n, m, k;
int s, t;
int Head[10001] = {0};
int Next[100001] = {0};
int To[100001] = {0};
int Weight[100001] = {0};
int cnt = 1;
int Distance[10001][11];
bool poped[10001][11] = {false};
int get_ans(int begin, int end, int k){
    int cur_city, cur_k, cur_cost, next_city, to_cost;
    auto cmp = [](vector<int> v1, vector<int> v2)->bool{
        return v1[2] > v2[2];
    };
    priority_queue<vector<int>, vector<vector<int>>, decltype(cmp)> q(cmp);
    vector<int> frist;
    frist.push_back(begin);
    frist.push_back(k);
    frist.push_back(0);
    Distance[begin][k] = 0;
    q.push(frist);
    while (!q.empty())
    {
        cur_city = q.top()[0];
        cur_k = q.top()[1];
        cur_cost = q.top()[2];
        q.pop();
        if(cur_city == end){
            return cur_cost;
        }
        if(poped[cur_city][cur_k] == true){
            continue;
        }
        poped[cur_city][cur_k] = true;
        for(int next = Head[cur_city+1];next != 0;next = Next[next]){
            next_city = To[next]-1;
            to_cost = Weight[next];
            if(cur_k > 0 && Distance[next_city][cur_k-1] > Distance[cur_city][cur_k]){
                Distance[next_city][cur_k-1] = Distance[cur_city][cur_k];
                vector<int> temp;
                temp.push_back(next_city);
                temp.push_back(cur_k-1);
                temp.push_back(Distance[cur_city][cur_k]);
                q.push(temp);
            }
            if(Distance[next_city][cur_k] > Distance[cur_city][cur_k] + to_cost){
                Distance[next_city][cur_k] = Distance[cur_city][cur_k] + to_cost;
                vector<int> temp;
                temp.push_back(next_city);
                temp.push_back(cur_k);
                temp.push_back(Distance[cur_city][cur_k] + to_cost);
                q.push(temp);
            }
        }
    }
    return 0;
}
void build(){
    for(int i = 0;i < 10001;++i){
        for(int j = 0;j <= 10;++j){
            Distance[i][j] = -((1 << 31) + 1);
        }
    }
}
int main(void){
    int a, b, c;
    scanf("%d%d%d", &n, &m, &k);
    scanf("%d%d", &s, &t);
    for(int i = 0;i < m;++i){
        scanf("%d%d%d", &a, &b, &c);
        Next[cnt] = Head[a+1];
        Head[a+1] = cnt;
        To[cnt] = b+1;
        Weight[cnt] = c;
        ++cnt;
        Next[cnt] = Head[b+1];
        Head[b+1] = cnt;
        To[cnt] = a+1;
        Weight[cnt] = c;
        ++cnt;
    }
    build();
    printf("%d", get_ans(s, t, k));
}

其中,采用链式前向星建图;主体流程和之前分层图最短路一样,只是for循环中需要注意一下。

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

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

相关文章

redis分布式锁(看门枸机制)

分布式锁确保在同一时间只有一个节点能获得对共享资源的独占访问权限&#xff0c;从而解决并发访问问题。 Redisson锁(简称看门狗) 它可以实现锁的延长&#xff0c;确保某个线程执行完才能让其他线程进行抢锁操作 引入看门狗机制后 如何使用&#xff1f; 1、引入依赖包 <…

大厂程序员的健身之路

大厂程序员的健身之路 基本信息饮食正餐营养补剂 睡眠训练计划 基本信息 健身时间&#xff1a;2023.03 -> 2024.09体重变化&#xff1a;52kg -> 67kg 饮食 正餐 早餐&#xff1a;不吃午餐&#xff1a;两碗米饭 鱼/鸡肉 蔬菜 酸奶晚餐&#xff1a;两碗米饭 鱼/鸡肉…

《史上最简单的 SpringCloud 教程》

Finchley版本 Spring Cloud Finchley; Spring Boot 2.0.3 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现&#xff08;Eureka&#xff09;(Finchley版本)史上最简单的SpringCloud教程 | 第二篇: 服务消费者&#xff08;restribbon&#xff09;(Finchley版本)史上最…

栈的各种接口的实现(C)

栈的概念 栈&#xff1a; 一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则。压栈&#xff1a;…

xtop:multi_driven_net与incomplete_timing_cell fail reason 分析

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 xtop做时序收敛时报告fail reason&#x

Cortex_M0开发学习_1

一、简介 意法半导体基于Arm Cortex-M0的STM32F0系列器件实现了32位性能&#xff0c;同时传承了STM32系列的重要特性&#xff0c;特别适合成本敏感型应用。STM32F0 MCU集实时性能、低功耗运算和STM32平台的先进架构及外设于一身。 STM32F0系列产品基于Cortex-M0内核&#xff0c…

基于SpringBoot的中小医院管理系统

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

MACCMS 远程命令执行漏洞复现(CVE-2017-17733)

目录 漏洞介绍 工具使用 环境搭建&复现过程 这是我复现的第一个漏洞&#xff08;老天奶&#xff09;&#xff0c;有必要做一个详细的writeup。 漏洞介绍 MACCMS是一套采用PHP/MySQL数据库运行的全新且完善的强大视频电影系统。完美支持众多视频网站和高清播放器(youku,…

Vue3实战:使用 errorHandler 捕获全局错误

你好同学&#xff0c;我是沐爸&#xff0c;欢迎点赞、收藏、评论和关注。 在 Vue3 中&#xff0c;app.config.errorHandler 是一个错误处理器&#xff0c;用于为应用内抛出的未捕获错误指定一个全局处理函数&#xff0c;它接收三个参数&#xff1a;错误对象、触发该错误的组件…

一起对话式学习-机器学习03——模型评估与模型选择

【一】前言 这一部分其实已在第二节中介绍到&#xff0c;这节起到回顾归纳的作用。 【二】训练误差与测试误差 首先&#xff0c;在分类问题中&#xff0c;有误差率和准确率两个概念&#xff0c;二者和为1。 误差率&#xff1a;分类错误的样本数占总数的比例。 其次&#xff0c…

数仓工具:datax

datax可以理解为sqoop的优化版&#xff0c; 速度比sqoop快 因为sqoop底层是map任务&#xff0c;而datax底层是基于内存 DataX 是一个异构数据源离线同步工具&#xff0c;致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各种异构数据源之间稳定…

4款音频转文字在线转换工具帮你解锁新的记录模式。

越来越多的人都知道使用一些工具来将音频直接转换成文字&#xff0c;这样便省去了手动输入的麻烦。而且使用音频进行记录也能够提高工作的效率&#xff0c;像会议记录&#xff0c;课堂教学记录&#xff0c;采访录音等。如果大家有需要将自己的音频转成文字&#xff0c;可以试试…

PDF——压缩大小的方法

方法一&#xff1a;QQ浏览器->格式转换->PDF转纯图PDF

【C++】STL----stack和queue常见用法

&#x1f525;个人主页&#x1f525;&#xff1a;孤寂大仙V &#x1f308;收录专栏&#x1f308;&#xff1a;C从小白到高手 &#x1f339;往期回顾&#x1f339;&#xff1a;【C】list常见用法 &#x1f516; 流水不争&#xff0c;争的是滔滔不息。 文章目录 一、stack的介绍s…

KTH7823——16 位高精度低延时霍尔磁编码器可编程 ABZ 和 PWM 输出模式角度传感器

KTH7823 是一款高精度绝对角度霍尔传感器芯片&#xff0c;最高 16 位分辨率绝对角度输出&#xff0c;可 实现在轴向和离轴场合下的无接触式磁场角度测量。不论转速范围在 0-120000rpm 之间&#xff0c; KTH7823 都能快速准确地输出角度信息&#xff0c;适用于需要精准角…

7个提升网站分页体验的 CSS 和 JavaScript 代码片段

文章目录 前言正文1.简洁直观的悬停分页效果2.实时显示页码的分页3.适合响应式设计的多功能分页4.专为移动设备优化的分页5.无数字的极简分页设计6.触屏友好的分页7.结合无限滚动与分页的设计 总结 前言 分页是内容丰富的网站中不可缺少的导航工具&#xff0c;能帮助用户更轻松…

鱼类计数与识别系统源码分享

鱼类计数与识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer V…

【C语言】⾃定义类型:联合和枚举

⾃定义类型&#xff1a;联合和枚举 1. 联合体1.1 联合体类型的声明1.2 联合体的特点1.3 相同成员的结构体和联合体对⽐1.4 联合体⼤⼩的计算1.5 联合的⼀个练习 2. 枚举类型2.1 枚举类型的声明2.2 枚举类型的优点2.3 枚举类型的使⽤ 1. 联合体 1.1 联合体类型的声明 像结构体…

滚珠花键与滚珠丝杆的区别与应用

在机械工业中&#xff0c;经常使用滚珠花键这种传动元件&#xff0c;人们经常拿它与滚珠丝杆相比较&#xff0c;甚至与之混淆。事实上&#xff0c;它们是不同的&#xff0c;滚珠花键和滚珠丝杆在机械传动领域中各有其独特的作用和特点。那么&#xff0c;两者之间的区别是什么呢…

list(二) (list模拟实现)

首先进行大框架 先写基本的结点类 有data next prev template<class T>class ListNode//或者使用struct 就不用在写public声明公有{public://这里不仅仅是成员函数 成员变量也要公有化 ListNode<T>* _next;ListNode<T>* _prev;T _data;}之后是链表list类…