【刷题笔记】网格图DFS/BFS总结 + Dijkstra用法

98 阅读16分钟

一、DFS 与 BFS

DFS : 一条路走到尽头,走不通再往回退(深度优先)

BFS :找完同层后继续往外(下一层)去找(广度优先)

DFS(深搜)和 BFS(宽搜)在网格图(后面简称图)中与树中进行搜索最大不同在于图中可以向8个方向进行搜索,而树只需要关注当前节点的左右孩子。

对于这类题,大致分为以下几类:

1、连通区域统计(Flood Fill 洪水填充)

2、可达性问题

3、无权图最短路(单源BFS)

4、多源BFS

5、带状态的BFS

6、回溯DFS(枚举所有路径)

1、连通区域统计(Flood Fill 洪水填充)

这一类题形如网格中有1(可走)、0等数字组成的区域,求岛屿数量、最大岛屿面积、封闭岛屿等。

解题方法 : DFS / BFS

DFS:代码篇幅短,但是网格很大(1000 x 1000)递归会栈溢出,此时需要用迭代DFS或者BFS

BFS :更加稳定,但是代码篇幅稍长

标记方式 :1、vis二维数组(不修改原数组)

2、原地修改 :将访问过的1改为0,节省空间

访问一个格子后,进行标记后就不需要撤销标记,这是与回溯最大的区别

实现示例:

#include <iostream>
#include <vector>

using namespace std;

//方向数组
int dx[4] = {-1, 0, 0, 1};
int dy[4] = {0, 1, -1, 0};
int m, n;
vector<vector<char>> grid;
vector<vector<bool>> vis; 

void dfs(int x, int y) {
	vis[x][y] = true;   //标记已经访问过的格子(也可以进行原地修改)
	
	for(int i = 0; i < 4; i++) {
		int nx = x + dx[i];
		int ny = y + dy[i];
		
		if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue; //判断是否越界
		if(grid[nx][ny] == '1' && !vis[nx][ny]){    //跳过已访问的格子和非1格子
			dfs(nx, ny);
         }
    }
}

int main() {
	cin >> m >> n;
	grid.resize(m, vector<char>(n));
	vis.resize(m, vector<bool>(n, false));
	
	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			cin >> grid[i][j];
		}
	}
	
	int ans = 0;
	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			if(grid[i][j] == '1' && !vis[i][j]) {
				dfs(i,j);
				ans++;
			}
		}
	}
	cout << ans << endl;
	return 0;
} 

非递归(迭代DFS)写法:

当数据规模极大,递归层数过深,导致超出了系统的默认栈容量(通常只有几MB),这个时候选择自己手动实现递归(手写Stack),而自己在堆上进行模拟栈(堆上空间可达数百MB甚至GB),故可以支持更深层次的递归。

//实现手写栈还需引入头文件<stack>

void dfs(int x, int y) {
	stack<pair<int, int>> st;
	st.push({x, y});
	vis[x][y] = true;
	
	while(!st.empty()){                     //判断栈是否为空,不为空进行操作
		pair<int, int> cur = st.top();  //获取栈顶元素
		int cx = cur.first;
		int cy = cur.second;
		st.pop();
		for(int i = 0; i < 4; i++) {
		int nx = cx + dx[i];
		int ny = cy + dy[i];
		
		if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
		if(grid[nx][ny] == '1' && !vis[nx][ny]){
			vis[nx][ny] = true;
			st.push({nx, ny});
                }
		}	
     }
}

BFS写法:

#include <iostream>
#include <vector>
#include <queue>

using namespace std;
 
int dx[4] = {-1, 0, 0, 1};
int dy[4] = {0, 1, -1, 0};
int m, n;
vector<vector<char>> grid;
vector<vector<bool>> vis;

void bfs(int x, int y) {
	queue<pair<int, int>> q;
	q.push({x,y});
	vis[x][y] = true;  
	
	while(!q.empty()) {
		pair<int, int> t = q.front();   //获取队头元素
		q.pop();       //弹出队头元素,为下一次入队做准备
		int x = t.first;
		int y = t.second;
		
		for(int i = 0; i < 4; i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];
			
			if(nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
			if(grid[nx][ny] == '1' && !vis[nx][ny]) {
				vis[nx][ny] = true;
				q.push({nx, ny});
			}
	        }
     }
}

int main() {
	int sx, sy;
	cin >> m >> n;
	cin >> sx >> sy;
	
	grid.resize(m, vector<char>(n));
	vis.resize(m,vector<bool>(n, false));
	
	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			cin >> grid[i][j];
		}
	}
	int ans = 0;
	for(int i = 0; i < m; i++) {
		for(int j = 0; j < n; j++) {
			if(grid[i][j] == '1' && !vis[i][j]){
				ans++;              //启动bfs时进行ans计数,若在bfs中计数则会变成统计岛屿面积
				bfs(i, j); 
			}
		}
	}
	cout << ans << endl;
	
	return 0;
} 

相关例题:200. 岛屿数量 - 力扣(LeetCode)

3619. 总价值可以被 K 整除的岛屿数目 - 力扣(LeetCode)

2658. 网格图中鱼的最大数目 - 力扣(LeetCode)

1254. 统计封闭岛屿的数目 - 力扣(LeetCode)

2、可达性问题

这类问题的核心是搜索 + 标记,从起点出发,能否通过合法路径到达目标区域是这类问题的常见形式。

如:

1.起点固定,问能否到达某一点(迷宫出口问题)

2.从边界/内部出发,问最大覆盖区域

实现代码与连通区域统计中相似,对合法的格子进行题目所要求的更改或标记。

例题中733的实现:

```
#include <iostream>
#include <vector>

using namespace std;

    int m, n;
    int originColor;
    int dx[4] = { -1,1,0,0 };
    int dy[4] = { 0,0,1,-1 };

    void dfs(vector<vector<int>>& image, int x, int y, int newColor) {

        //判断初始点是否越界
        if (x < 0 || x >= m || y < 0 || y >= n) return;

        //判断是否是同型连通块
        if (image[x][y] != originColor) return;

        //标记为渲染后的新颜色
        image[x][y] = newColor;

        for (int i = 0; i < 4; i++) {
            int nx = x + dx[i];
            int ny = y + dy[i];
            dfs(image, nx, ny, newColor);
        }
    }

    vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
        m = image.size();
        n = image[0].size();

        //记录初始颜色
        originColor = image[sr][sc];

        //要渲染的颜色与初始相同,直接返回,避免死循环
        if (originColor == color) return image;

        dfs(image, sr, sc, color);

        return image;
    }

    int main() {

        cin >> m >> n;
        vector<vector<int>> image(m, vector<int>(n));

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                cin >> image[i][j];
            }
        }

        vector<vector<int>> res = floodFill(image, 1, 1, 2);

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                cout << res[i][j];
                if (j < n - 1) cout << " ";
            }
            cout << endl;
        }
        return 0;

    }

相关例题 :733. 图像渲染 - 力扣(LeetCode)

130. 被围绕的区域 - 力扣(LeetCode)

3、无权图最短路(单源BFS)

此类最短路问题中使用的BFS需要加入“层”,由近及远。

相关题目:1091. 二进制矩阵中的最短路径 - 力扣(LeetCode)(下附实现)

P1443 马的遍历 - 洛谷

代码实现:

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

int m, n;
int dx[8] = { -1, -1, -1, 0, 0, 1, 1, 1 };
int dy[8] = { 0, 1, -1, 1, -1, 1, 0, -1 };

int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
    m = grid.size();
    n = grid[0].size();

    queue<pair<int, int>> q;
    if (grid[0][0] != 0 || grid[m - 1][n - 1] != 0)
        return -1;
    q.push({ 0, 0 });
    grid[0][0] = 1;  //原地标记

    //记录当前层数
    int steps = 1;

    while (!q.empty()) {
        int sz = q.size();
        for (int i = 0; i < sz; i++) {
            auto t = q.front();
            q.pop();
            int x = t.first;
            int y = t.second;

            if (x == m - 1 && y == n - 1)
                return steps;

            for (int i = 0; i < 8; i++) {
                int nx = x + dx[i];
                int ny = y + dy[i];

                if (nx < 0 || nx >= m || ny < 0 || ny >= n)
                    continue;
                if (grid[nx][ny] != 0)
                    continue;

                grid[nx][ny] = 1;
                q.push({ nx, ny });
            }
        }
        steps++;  //搜完一层steps自增进入下一层搜索
    }
    return -1;
}

int main() {
    cin >> m >> n;
    vector<vector<int>> grid(m,vector<int>(n));

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> grid[i][j];
        }
    }

    int res = shortestPathBinaryMatrix(grid);

    cout << res << endl;

	return 0;
}

4、多源BFS

顾名思义,多源(即起点),把多个起点放入队列中进行BFS,同时扩散,其写法与单源BFS大致相同,只在入队处稍稍改变。

核心 : 多源BFS = 多个起点入队 + 层数统计 + 正确标记

相关题目:994. 腐烂的橘子 - 力扣(LeetCode)

542. 01 矩阵 - 力扣(LeetCode)

腐烂橘子实现:

#include <iostream>
#include <queue>

using namespace std;

int m, n;
int dx[4] = { 0, 1, -1, 0 };
int dy[4] = { -1, 0, 0, 1 };

int orangesRotting(vector<vector<int>>& grid) {
	m = grid.size();
	n = grid[0].size();
	int fresh = 0;   //新鲜橘子的个数

	queue<pair<int, int>> q;

	for (int i = 0; i < m; i++) {
		for (int j = 0; j < n; j++) {
			if (grid[i][j] == 2) {
				q.push({ i, j });   //将所有腐烂橘子入队
			}
			else if (grid[i][j] == 1) {
				fresh++;
			}
		}
	}

	if (fresh == 0) return 0;

	int minutes = 0;
	while (!q.empty()) {
		int sz = q.size();        //当前层数
		bool infected = false;    //标记当前层是否有感染发生
		for (int i = 0; i < sz; i++) {
			auto t = q.front();
			q.pop();
			int x = t.first;
			int y = t.second;

			for (int k = 0; k < 4; k++) {
				int nx = x + dx[k];
				int ny = y + dy[k];

				if (nx < 0 || nx >= m || ny < 0 || ny >= n || grid[nx][ny] != 1) continue;

				grid[nx][ny] = 2;
				infected = true;
				fresh--;
				q.push({ nx, ny });

			}
		}
		if (infected) minutes++;
	}
	return fresh ? -1 : minutes;
}



int main() {
	cin >> m >> n;

	vector<vector<int>> grid(m, vector<int>(n));
	for (int i = 0; i < m; i++) {
			for (int j = 0; j < n; j++) {
				cin >> grid[i][j];
			}
		}

	int res = orangesRotting(grid);

	cout << res << endl;
	return 0;
}

对上面代码一些地方进行一些说明:

int sz = q.size(); 这一行要写在while循环中,如果写在外面,则sz的值被固定了,无法实现正确的层序变更。

如:

2 1 1

1 1 2

1 1 1

这个例子来进行模拟实现: 一开始将(0,0)和(1,2)的腐烂橘子入队,此时sz = 2,进行第一轮感染(while第一次循环),(0,1),(1,0),(1,1),(0,2),(2,2)位置会被感染,此时布局变更为:

2 2 2

2 2 2

1 1 2

minutes = 1

在infected标记中为: (T表示true,F表示flase, 图中不加粗F代表初始感染源)

F T T

T T F

F F T

此时腐烂橘子(保留感染能力)为5,即正确的sz也应等于5,可sz在while外,无法正确更新,下一轮while按队列顺序弹出队头2个((0,1)和(1,0)),得到:

2 2 2

2 2 2

2 1 2

(0,1)无感染,(1,0)感染(2,0), 此时:

· 队列:[(1, 1), (0, 2), (2, 2), (2, 0)]

minutes = 2

同理,下一轮while取前两个队头元素,最后minutes为3(实际上答案应为2,正确实现在第2次while循环就已经完成)。

这里还是在程序没有崩溃的前提下,模拟出的结果。

image.png

这是将sz放在while外面产生的报错(front() called on empty deque),试图在一个空队列上调用q.front()。

原因在于while(!q.empty())只能保证循环开始时队列不为空,无法保证在循环过程中for (int i = 0; i < sz; i++)执行时队列不为空,当执行到某个地方时,由于q中元素不足2个,会触发assert断言

!!!所以要把sz放入while中,让每一轮只处理当前层 !!!

5、带状态的BFS

相比于其他常规的BFS,这里引入了一个新的维度——“状态”,所以我们要把标记数组visited进行升维(变成三维数组)visited[][][],前两维仍表示位置,第三维表示状态,这需要我们记录一定要用到的量,其余BFS过程亦可套用模板。

相关题目:864. 获取所有钥匙的最短路径 - 力扣(LeetCode)

做这道题之前,需要了解一些基本的位运算符,如:

1. &(按位与):两个都是1,结果才是1

1 0 1 & 0 1 1

1 0 1

0 1 1

0 0 1

每位按位对齐后可知第一位不全是1,第二位不全是1,第三位全是1, 得到结果0 0 1

2. |(按位异或):只要有一位是1,结果就是1

1 0 1 | 0 1 1

对齐后得到结果1 1 1

3. <<(左移):将二进制整体向左移()位

1 << 2

1的二进制数是0001,左移2位后为0100(4),所以结果为4

相关代码实现:

#include <iostream>
#include <vector>
#include <queue>
#include <tuple>
#include <bitset>
#include <string>

using namespace std;

int m, n;
int DIRS[4][2] = { {0, 1}, {0, -1}, {-1, 0}, {1, 0} };     // 上下左右
int shortestPathAllKeys(vector<string>& grid) {
    m = grid.size();
    n = grid[0].size();
    int sx = 0, sy = 0;    //起点坐标
    int totalKeys = 0;     //钥匙总数

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (grid[i][j] == '@') {
                sx = i;
                sy = j;
            }
            else if (grid[i][j] >= 'a' && grid[i][j] <= 'f') {  //将其转为掩码来记录是第几把钥匙
                totalKeys |= (1 << (grid[i][j] - 'a'));
            }
        }
    }

    queue<tuple<int, int, int>> q;
    
    //将totalKeys其转为32位的二进制形式,方便后面使用.count()方法
    int keyCount = bitset<32>(totalKeys).count(); 
    
    //数掩码中有多少个1
    vector<vector<vector<int>>> visited(m, vector<vector<int>>(n, vector<int>(1 << keyCount, 0)));  
    

    q.push({ sx,sy,0 });
    visited[sx][sy][0] = 1;

    int steps = 0;
    while (!q.empty()) {
        int sz = q.size();
        for (int i = 0; i < sz; i++) {
            auto [x, y, keys] = q.front();
            q.pop();

            if (keys == totalKeys) return steps;   //钥匙数与总数相等时,说明已找到全部钥匙,返回步数

            for (auto& d : DIRS) {
                int nx = x + d[0];
                int ny = y + d[1];

                if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue; //越界跳过
                if (grid[nx][ny] == '#') continue;    //碰墙跳过

                int nk = keys;

                if (grid[nx][ny] >= 'A' && grid[nx][ny] <= 'F') {     //遇到锁
                    if (!(nk & (1 << (grid[nx][ny] - 'A')))) continue;   //但是没有对应钥匙,跳过
                }

                if (grid[nx][ny] >= 'a' && grid[nx][ny] <= 'f') {  //遇到钥匙
                    nk |= (1 << (grid[nx][ny] - 'a'));  //改变对应掩码,更新钥匙对应情况
                }

                if (!visited[nx][ny][nk]) {    //该坐标及钥匙状态没被访问
                    visited[nx][ny][nk] = 1;   //标记已访问
                    q.push({ nx,ny,nk });
                }
            }
        }
        steps++;  //更新步数
    }
    return -1;    //无法拿到全部钥匙,返回-1
}

int main() {

    cin >> m;
    vector<string> grid(m);
    for (int i = 0; i < m; i++) {
            cin >> grid[i];
    }
    int ans = shortestPathAllKeys(grid);
    cout << ans << endl;
	return 0;
}

代码中的一些说明:

1.tuple的说明:tuple是C++标准库中的元组(能够将不同类型的数据打包为一个对象)

与pair的差别:pair是一个二元组(两个成员),tuple是多元组(2、3、4……)

2.bitset的说明:bitset是固定大小 位集合,大小必须是常量!

int n = 1000;
bitset<n> bs; //这里会编译报错!因为n是变量

.count()方法是用来数二进制数中有多少个1

3.totalKeys的含义:这里通过 |= 运算将其转为一个二进制数(位掩码)

totalKeys |= (1 << (grid[i][j] - 'a'));

将遇到的钥匙记录到掩码totalKeys中(比如遇到‘a’则第0位变为1)。

这道题难点在于弄清楚有哪些必需量,通过钥匙-锁的对应状态来设计BFS中的实现步骤,与DP(动态规划) 有共同之处,都需要想清楚状态,不过DP中难点在于理清状态的转移,写出相应的递推式 。

6、回溯DFS(枚举所有路径)

上面的5种题型都用到了vis标记数组(或原地修改),回溯与之不同的是标记是可以撤销的(即恢复现场),当有多种选择都能够实现目标时,则选择其一分支(将vis标记为1,表示已访问),完成后将vis改为0(恢复未访问状态),实现回溯。

void dfs(int x, int y) {
······(前面一些判断逻辑)
vis[nx][ny] = 1;  //标记为已访问(1)
dfs(nx, ny);      //递归进入下一轮
vis[nx][ny] = 0;  //恢复现场,改为未访问状态(0)
}

相关题目:2684. 矩阵中移动的最大次数 - 力扣(LeetCode)

实现:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int m, n;
int dx[3] = { -1, 0, 1 };
int dy[3] = { 1, 1, 1 };
int maxStep;

void dfs(vector<vector<int>>& grid, int x, int y, int step, vector<vector<bool>>& vis) {
    maxStep = max(maxStep, step);

    for (int i = 0; i < 3; i++) {
        int nx = x + dx[i];
        int ny = y + dy[i];

        if (nx < 0 || nx >= m || ny < 0 || ny >= n) continue;
        if (vis[nx][ny]) continue;
        if (grid[nx][ny] <= grid[x][y]) continue;

        vis[nx][ny] = true;
        dfs(grid, nx, ny, step + 1, vis);
    }
}

int maxMoves(vector<vector<int>>& grid) {
    m = grid.size();
    n = grid[0].size();
    maxStep = 0;
    vector<vector<bool>> vis(m, vector<bool>(n, false));

    //回溯实现
    for (int i = 0; i < m; i++) {
        vis[i][0] = true;
        dfs(grid, i, 0, 0, vis);
        vis[i][0] = false;
    }
    return maxStep;
}

int main() {
    int m, n;
    cin >> m >> n;
    vector<vector<int>> grid(m, vector<int>(n));

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            cin >> grid[i][j];
        }
    }

    int ans = maxMoves(grid);
    cout << ans << endl;
	return 0;
}

二、Dijkstra(迪杰斯特拉算法)

Dijkstra适用于单源带权有向图最短路(所有边的权重必须是非负数),算法本质是利用“优先队列”来实现 。

一般的队列满足FIFO(先进先出),而优先队列则是弹出当前队列中最小的元素,这么看来就是:

优先队列 = 贪心 + 队列

在此就不赘述贪心了,我们来聊聊为什么边的权重为什么不能是负数 。

如果存在负权边,则会出现下列情况:

A -> B (10)
A -> C (1)
C -> B (-5)

我们看上面这个例子,A作为起点,刚开始优先队列中只有A,弹出A后将邻居B、C进行入队,AC边权小,C出队,后从C出发,此时负权边最小,弹出。

站在优先队列角度看,队列变化如下:

{ (0, A) } ->弹A,松弛 -> { (10, B), (1, C) } ->弹C,松弛B(1-5=-4 ) -> { (-4, B), (10, B) } -> 弹B(-4),确定最短

距离:0 -> 1 -> -4

但是注意Dijkstra是存在贪心的,即只选取当前情况下代价最小元素弹出,弹出即锁定 。

但是在弹完C后(距离为1)算法锁定,如果后续还存在能够绕回C的负权路径,C会被错误锁定,这里B的-4在C弹出后才进行入队,-4小于10,使B被更新为一个小值,这个时候未弹出节点会被负权边持续刷新,导致贪心锁被破坏,Dijkstra失效

代码实现:

#include <iostream>
#include <vector>
#include <queue>
#include <climits>

using namespace std;

//邻接表:graph[u] = {v, weight}
vector<vector<pair<int, int>>> graph;  //用来存所有从u出发的边
vector<int> dist;                      //距离表,用来存从起点到u的当前最短距离, 后续会不断被更新

void dijkstra(int start) {
    int n = graph.size();              //节点总数
    dist.assign(n, INT_MAX);           //将初始距离初始化为无穷大
    dist[start] = 0;                   //起点距自己距离为0
    
    //优先队列:{距离,节点},默认大顶堆,用greater变成小顶堆
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
    pq.push({0, start});
    
    while(!pq.empty()) {
        auto [d, u] = pq.top();
        pq.pop();
        
        // 如果弹出的距离大于当前记录的距离,说明这个节点已经处理过了
        if(d > dist[u]) continue;
        
        //松弛处理(不断用更短的路径去更新邻居)
        for(auto [v, w] : graph[u]) {    //遍历u的所有出边{v,w}
        if(dist[u] + w < dist[v]) {      //dist[u] + w是过u达v的距离
            dist[v] = dist[u] + w;
            pq.push({dist[v], v});
            } 
        }
    }
}

补充一下为什么会锁定,即当start弹出后,将邻居带有的边权值加入队列中距离要素, 此时贪心弹出队列中代价最小的元素,弹出后,即便后续能够通过另一条路径连通(前提无负权边),但在贪心弹出这一步就已经锁定,后续无法把这最短路径变得更小

c0e566241088daa17b760ba5d3f482e6.jpg

相关题目:743. 网络延迟时间 - 力扣(LeetCode)

代码实现:

#include <iostream>
#include <vector>
#include <queue>
#include <climits>
#include <algorithm>

using namespace std;

int networkDelayTime(vector<vector<int>>& times, int n, int k) {
    vector<vector<pair<int, int>>> g(n + 1);          //建立邻接表,g[x]存所有从x出发的边
    for (auto& t : times) {
        int x = t[0], y = t[1], wt = t[2];
        g[x].emplace_back(y, wt);
    }

    vector<int> dis(n + 1, INT_MAX);                  //dis[i]表示从起点到节点i的最短距离,初始化为无穷大
    
    //priority_queue<元素类型,实现容器,比较器>
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;    
    dis[k] = 0;                                                             //起点到自己的距离为0
    pq.emplace(0, k);                                                       //把起点丢进队列

    while (!pq.empty()) {
        auto [dis_x, x] = pq.top();
        pq.pop();
        
        //同一个节点可能会重复入队,如果弹出的dis_x大于之前记录的dis[x],说明之前的最小距离已经处理过了,跳过
        if (dis_x > dis[x]) continue;      
        
        //松弛操作
        for (auto& [y, wt] : g[x]) {       //遍历x的所有出边{y,wt}
            int new_dis_y = dis_x + wt;    //从起点过x再到y的距离
            if (new_dis_y < dis[y]) {
                dis[y] = new_dis_y;
                pq.emplace(new_dis_y, y);
            }
        }
    }

    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (dis[i] == INT_MAX) return -1;
        ans = max(ans, dis[i]);
    }
    return ans;
}

int main() {
    int n, k, m;
    cin >> n >> k >> m;     //节点数、起点、边数
    vector<vector<int>> times(m, vector<int>(3));

    for (int i = 0; i < m; i++) {
        cin >> times[i][0] >> times[i][1] >> times[i][2];
    }

    int ans = networkDelayTime(times, n, k);
    cout << ans << endl;
	return 0;
}

补充:

1.对priority_queue的解释:priority_queue的三个参数是"存什么","用什么存","怎么排序"

在这里存{距离, 节点},优先队列的内部要一个容器用来存储数据,至于为什么用vector,可以理解为队列的底层是用数组实现的,priority_queue默认是大顶堆,而Dijkstra则需要每次弹出队列中最小的元素,所以要用greater<>转为小顶堆

查阅了一些资料,如果存在负权边,Dijkstra失效,此时要用Bellman-Ford或SPFA解决

好了,近3个星期对这一块内容的总结和理解,还可能有很多地方表述不是很准确,一些地方表述不准确使用了AI进行纠错和润色,第一次写对某一个算法的全面总结,有不好的地方欢迎大家指出和补充

oEDEGI2ZQA3lgcMVYftRmyzBsAYcmhZX5AhNAE.gif