流 泪 道 别 并 不 适 合|

Ydoc770

园龄:6个月粉丝:6关注:13

2024-10-09 15:56阅读: 8评论: 0推荐: 0

#4. 图的存储、最短路(未完结)

bro高一才开始自学图论(未完结警告)

图的存储

建议无脑用链式前向星

0x01. 什么是链式前向星

定义(摘自OI wiki)

本质上是用链表实现的邻接表

具体来说:

以有向边的形式 ,head 数组存当前边的编号,e[i].nxt 数组存上一次加的以 u 为起点的边的编号,这样就能实现用 head[u]e[i].nxt 遍历所有出边;v 存边的终点; w 存边权

加边操作:

void add (int u, int v, int w) {
e[++tot].nxt = head[u];
e[tot].v = v;
e[tot].w = w;
head[u] = tot;
}

找是否存在 uv 的边:

bool find(int u, int v) {
for(int i = head[u]; i; i = e[i].nxt) {
if (e[i].v == v) {
return true;
}
}
return false;
}

遍历一个节点连向的所有出边:

for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].v, w = e[i].w;
...
}

遍历图:

void dfs(int u) {
if(vis[u]) return;
vis[u] = true;
for(int i = head[u]; i; i = e[i].nxt) {
...
dfs(e[i].v);
}
}

0x02. 性质与复杂度

·可以通过每次多存一条反边来存储树或无向图,复杂度 O(m)
·可以查询是否存在 uv 的边,复杂度 O(u)
·可以遍历点 u 的所有出边,复杂度 O(u)
·可以遍历整张图,复杂度 O(n+m)
·空间复杂度 O(m)

非负权图单源最短路: Dijkstra 算法

0x01. 算法思路

s 表示起点节点编号,dist 数组记录起点到每个点的最短路径,实现流程如下:

  1. 初始化起点 dist[s]0,其他节点 dist[i] 为无穷大;
  2. 找出未被遍历的,dist 值最小的 dist[u],标记 u
  3. 扫描 dist[u] 所有出边 (w,v),对于 dist[v],若 dist[u]+wi<dist[v] 则更新 dist[v]
  4. 重复 23 步骤直到所有节点被标记。

Dijkstra 基于贪心思想,它只适用于边权非负的图。当边权 wi 出现负数时,全局最小值 dist[u] 还可能被更新,不再符合贪心的思路;而在 wi 非负时则不会出现这种情况,dist[u] 已经成为 su 的最短路,这样便可以保证每次更新一定在拓展最短路,最终一定能得到 s 到每个节点的最短路(保证图连通的情况下)。

0x02. 算法实现与复杂度

P3371 【模板】单源最短路径(弱化版)

对于朴素的 Dijkstra,每次遍历所有节点找到 dist[u] 最小值 O(n),保证每次更新能确定 s 到一个节点的最短路,则总复杂度 O(n2)
AC代码:

#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 1e4 + 10;
int n, m, s;
struct edge{
int v, nxt, w;
}e[maxn<<6];
int tot = 0, head[maxn];
void add(int u, int v, int w) {
e[++tot].nxt = head[u];
head[u] = tot;
e[tot].v = v;
e[tot].w = w;
}
const int inff = 2147483647;
int d[maxn];
bool vis[maxn];
void dijkstra() {
for(int i = 0; i <= n; i++) d[i] = inff;
d[s] = 0;
for(int i = 1; i < n; i++) {
int u = 0;
for(int j = 1; j <= n; j++) {
if(!vis[j] && (u == 0 || d[j] < d[u])) u = j;
}
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w;
d[v] = min(d[v], d[u] + w);
}
}
return;
}
int main() {
n = read(), m = read(), s = read();
for(int i = 1; i <= m; i++) {
int u = read(), v = read(), w = read();
add(u, v, w);
}
dijkstra();
for(int i = 1; i <= n; i++) {
printf("%d ", d[i]);
}
return 0;
}

P4779 【模板】单源最短路径(标准版)

发现每次找最小 dist[u] 时不必一个一个遍历,可以把起点和每次更新过的 dist[v] 放在一个小根堆里,每次取出堆顶更新,复杂度 O(mlogn)
使用STL库中的优先队列实现,每次以 dist[v] 为第一关键字把大根堆变成小根堆。
AC代码:

#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 1e5 + 10;
struct edge {
int nxt, v, w;
}e[maxn<<1];
int n, m, s;
priority_queue<pair<int, int> >q;
int tot = 0, head[maxn];
void add(int u, int v, int w) {
e[++tot].nxt = head[u];
head[u] = tot;
e[tot].v = v;
e[tot].w = w;
}
const int inff = 2e9;
int d[maxn];
bool vis[maxn];
void dijkstra() {
for(int i = 1; i <= n; i++) d[i] = inff;
d[s] = 0;
q.push(make_pair(0, s));
while(!q.empty()) {
int u = q.top().second; q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v;
if(d[u] + e[i].w < d[v]) {
d[v] = d[u] + e[i].w;
q.push(make_pair(-d[v], v));
}
}
}
return;
}
int main() {
n = read(), m = read(), s = read();
for(int i = 1; i <= m; i++) {
int u = read(), v = read(), w = read();
add(u, v, w);
}
dijkstra();
for(int i = 1; i <= n; i++) {
printf("%d ", d[i]);
}
return 0;
}

注意:不要盲目选择优先队列优化 dijkstra ,当 m 达到 n2 数量级时,优先队列可能成为负优化

可检测负环单源最短路: Bellman–Ford 算法(以及SPFA)

0x01. 算法思路

0x02. 算法实现与复杂度

P3371 【模板】单源最短路径(弱化版)

AC代码:

#include<bits/stdc++.h>
using namespace std;
inline int read() {
int f = 1, otto = 0;
char a = getchar();
while(!isdigit(a)) {
if(a == '-') f = -1;
a = getchar();
}
while(isdigit(a)) {
otto = (otto << 1) + (otto << 3) + (a ^ 48);
a = getchar();
}
return f * otto;
}
const int maxn = 1e4 + 10;
const int maxm = 5e5 + 10;
int m, n, s;
struct edge{
int v, w, nxt;
}e[maxm];
int tot = 0, head[maxn];
void add(int u, int v, int w) {
e[++tot].nxt = head[u];
head[u] = tot;
e[tot].v = v;
e[tot].w = w;
}
const int inff = 2147483647;
queue<int> q;
int d[maxn];
bool vis[maxn];
void spfa() {
for(int i = 1; i <= n; i++) d[i] = inff;
d[s] = 0;
vis[s] = 1;
q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop();
vis[u] = 0;
for(int i = head[u]; i; i = e[i].nxt) {
int v = e[i].v, w = e[i].w;
if(d[v] > d[u] + w) {
d[v] = d[u] + w;
if(!vis[v]) vis[v] = 1, q.push(v);
}
}
}
}
int main() {
n = read(), m = read(), s = read();
for(int i = 1; i <= m; i++) {
int u = read(), v = read(), w = read();
add(u, v, w);
}
spfa();
for(int i = 1; i <= n; i++) printf("%d ", d[i]);
}

任意点对最短路: Floyd 算法

任意点对最短路:Johnson 算法

本文作者:Ydoc770

本文链接:https://www.cnblogs.com/Ydoc770/p/18454192

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   Ydoc770  阅读(8)  评论(0编辑  收藏  举报
评论
收藏
关注
推荐
深色
回顶
收起
点击右上角即可分享
微信分享提示