y总:n 表示点数 m表示边数
最短路: memset(d,0x3f,sizeof d); INF = 0x3f3f3f3f;
图的储存:稀疏图 邻接表,稠密图 邻接矩阵 (n^2 与 m 的数量级)
TLE时考虑 各种初始化 memset(dist,0x3f,sizeof dist);
用邻接表时一定要先初始化 memset(h,-1,sizeof h);
无向图 g[a][b] = g[b][a] = min(g[a][b],c); add(b,a,c); 注意:e[ ]、ne[ ]、w[ ] 数组范围多开一倍
互相比对来理解加深
dijkstra
朴素版dijkstra
#include <bits/stdc++.h>
using namespace std;
const int N = 1010,INF = 0x3f3f3f3f;
int n,m,sx,sy;
int g[N][N],dist[N];
bool st[N];
int dijkstra() // S集合:已确定的从sx点出发到点的距离是最短的
{
memset(dist,0x3f,sizeof dist);
dist[sx] = 0; // 以某个点作为出发点
// 如果 下面就会再次 赋为 true Segmentation Fault
//st[1] = true; (错误) 第一次 必定是 t = 1 因为 dist[1] = 0
for(int i = 0;i < n;i++) // 迭代n个点
{
int t = -1;
for(int j = 1;j <= n;j++) // 遍历 不在 S集合中的距离最小的点
if(!st[j] && (t == -1 || dist[j] < dist[t]))
t = j; // 找到距 t 最近的点
st[t] = true; // 将 t 加入 S集合中(当前已确定最短距离的点)
for(int j = 1;j <= n;j++)
dist[j] = min(dist[j],dist[t] + g[t][j]); // 用 t 更新
} // 与 堆优化版 distance 相比对
return d[sy];
}
int main()
{
cin >> n >> m >> sx >> sy;
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
if(i == j) g[i][j] = 0;
else g[i][j] = INF;
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
g[a][b] = min(g[a][b],c); // 如果无向图 直接增加 g[b][a] = min(g[b][a],c);
} // 或者 g[a][b] = g[b][a] = min(g[a][b],c);
int t = dijkstra();
if(t == 0x3f3f3f3f) cout << "-1" << endl;
else cout << t << endl;
return 0;
}
堆优化版dijkstra
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N = 1010,INF = 0x3f3f3f3f,M = 20010;
// 1000 * 2 + 10
int n,m,sx,sy;
int h[N],e[M],ne[M],idx,w[M];
int dist[N];
bool st[N];
priority_queue<PII,vector<PII>,greater<PII>> heap;
void add(int a,int b,int c)
{
w[idx] = c;
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int dijkstra()
{
memset(dist,0x3f,sizeof dist);
dist[sx] = 0;
heap.push({0,sx}); // first为距离 second为节点
// 强调 先后顺序不能变 first second
while(heap.size())
{
auto t = heap.top();
heap.pop();
int distance = t.first,ver = t.second;
// 同朴素版的 S集合
if(st[ver]) continue; // 此处直接调过该节点 所以下面的if 不能加
st[ver] = true;
for(int i = h[ver];i != -1;i = ne[i])
{
int j = e[i]; // w[i] i 是不断迭代的ne[i] 对应储存的 idx
if(dist[j] > distance + w[i]) // 注: 此处 if判断不能加 (!st[j] && )
{
dist[j] = distance + w[i];
heap.push({dist[j],j});
}
}
}
return dist[sy];
}
int main()
{
cin >> n >> m >> sx >> sy;
memset(h,-1,sizeof h);
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
add(a,b,c); // 如果是 无向图 add(a,b,c); add(b,a,c);
}
int t = dijkstra();
if(t == INF) cout << "-1" << endl;
else cout << t << endl;
return 0;
}
bellman_ford
#include <bits/stdc++.h>
using namespace std;
const int N = 510,INF = 0x3f3f3f3f,M = 10010;
struct Edge
{
int x,y,c;
}tu[M];
int n,m,k,sx,sy;
int dist[N],backup[N];
int bellman_ford()
{
memset(dist,0x3f,sizeof dist);
dist[sx] = 0;
for(int i = 0;i < k;i++) // 遍历k条边 最多不超过k条边
{
memcpy(backup,dist,sizeof dist);
for(int j = 0;j < m;j++) // 每个边都遍历一遍
{
int a = tu[j].x,b = tu[j].y,w = tu[j].c;
if(dist[b] > backup[a] + w) // 同dijkstra相对比来理解
dist[b] = backup[a] + w; // 用点a来更新点b
}
}
return dist[sy];
}
int main()
{
cin >> n >> m >> k >> sx >> sy;
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
tu[i] = {a,b,c};
}
int t = bellman_ford();
if(t > INF / 2) cout << "-1" << endl; // 如果存在负权边 无法到达 可能不是INF
else cout << t << endl;
return 0;
}
spfa
最短路
#include <bits/stdc++.h>
using namespace std;
const int N = 1010,INF = 0x3f3f3f3f,M = 20010;
int n,m,sx,sy;
int h[N],e[M],ne[M],idx,w[M];
int dist[N];
bool st[N];
queue<int> q;
void add(int a,int b,int c)
{
w[idx] = c;
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
int spfa()
{
memset(dist,0x3f,sizeof dist);
dist[sx] = 0;
q.push({sx});
st[sx] = true;
while(q.size())
{
auto t = q.front();
q.pop();
st[t] = false; // 若存在其它更短路径 还会再次入队
for(int i = h[t];i != -1;i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i]; // 发生重复加入之前会被st[]数组判掉
if(!st[j]) // 因为 更新距离在if(!st[j])之上 不会加入队列 但如果距离较小的话 仍然会被更新
{
st[j] = true;
q.push({j});
}
}
}
}
return dist[sy];
}
int main()
{
cin >> n >> m >> sx >> sy;
memset(h,-1,sizeof h);
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
add(a,b,c); add(b,a,c);
}
int t = spfa();
// 注意此处对于 不存在路径的判断
if(t == INF) cout << "-1" << endl; // 因为每次更新都是用之前已经更新过的节点 如果不存在负环则不考虑其它情况
else cout << t << endl;
return 0;
}
判断是否存在负环
#include <bits/stdc++.h>
using namespace std;
const int N = 2010,M = 10010;
int h[N],e[M],ne[M],idx,w[M];
int dist[N],cnt[N],n,m;
bool st[N];
queue<int> q;
void add(int a,int b,int c)
{
w[idx] = c;
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
bool spfa()
{
//memset(dist,0x3f,sizeof dist); 注: 理解 不需要初始化 及相应下面距离的更新
//dist[1] = 0;
for(int i = 1;i <= n;i++)
{
q.push(i);
st[i] = true;
}
while(q.size())
{
int t = q.front();
q.pop();
st[t] = false;
for(int i = h[t];i != -1;i = ne[i])
{
int j = e[i];
if(dist[j] > dist[t] + w[i])
{
dist[j] = dist[t] + w[i];
cnt[j] = cnt[t] + 1;
if(cnt[j] >= n) return true;
if(!st[j])
{
q.push(j);
st[j] = true;
}
}
}
}
return false;
}
int main()
{
memset(h,-1,sizeof h);
cin >> n >> m;
for(int i = 0;i < m;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
add(a,b,c);
}
if(spfa()) puts("Yes");
else puts("No");
return 0;
}
Floyd
#include <bits/stdc++.h>
using namespace std;
const int N = 210,INF = 1e9;
int g[N][N],n,m;
void floyd()
{
for(int k = 1;k <= n;k++) // k <= n
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
g[i][j] = min(g[i][j],g[i][k] + g[k][j]);
}
int main()
{
int T;
scanf("%d %d %d",&n,&m,&T);
for(int i = 1;i <= n;i++)
for(int j = 1;j <= n;j++)
if(i == j) g[i][j] = 0;
else g[i][j] = INF;
while(m--)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
g[a][b] = min(g[a][b],c);
}
floyd();
while(T--)
{
int x,y;
scanf("%d %d",&x,&y);
if(g[x][y] > INF / 2) puts("impossible");
else printf("%d\n",g[x][y]);
}
return 0;
}