最短路问题
记录
22:22 2024-1-31
目录
1.最短路
相关知识
Dijkstra:处理没有负边的情况。
Bellman-Ford:可以处理边是负数的情况。不能计算图中有负圈的情况,可以用来判断是否存在负圈。
SPFA:队列优化的 Bellman-Ford 算法
Floyd-Warshall:可以处理边是负数的情况。
1.单源最短路
m指边数 n指节点个数
1.Dijkstra
O(n^2)
点击查看代码
const int INF = 0x3f3f3f3f;
int graph[MAX_V][MAX_V];
int d[MAX_V]; //距离
bool used[MAX_V]; //是否使用过
int V; //节点个数
void dijkstra(int s) {
fill(d, d + V, INF);
fill(used, used + V, false);
d[s] = 0;
while (true) { //每个节点最多访问一次 所以最多循环V次
int v = -1;
//查找最小的节点
for(int u = 0; u < V; u++) {
if(!used[u] && (v == -1 || d[u] < d[v])) v = u;
}
if(v == -1) break;
used[v] = true;
for(int u = 0; u < V; u++) {
d[u] = min(d[u], d[v] + graph[v][u]);
}
}
}
O(mlogn)
这个优化是利用最小堆来获得目前的最短路径
利用vector
点击查看代码
struct edge{int to, cost;};
typedef pair<int, int> P; //first 最短距离 second顶点编号
int V;
vector<edge> G[MAX_V];
int d[MAX_V];
void dijkstra(int s) {
//堆按照first从小到大排序
priority_queue<P, vector<P>, greater<P> > que;
fill(d, d + V, INF);
d[s] = 0;
que.push(P(0, s));
while (!que.empty()) {
P p = que.top(); que.pop();
int v = p.second;
if(d[v] < p.first) continue;
for(int i = 0; i < G[v].size(); i++) {
edge e = G[v][i];
if(d[e.to] > d[v] + e.cost) {
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to], e.to));
}
}
}
}
利用邻接表
点击查看代码
const int N = 100010, M = 1000010;
int head[N], ver[M], edge[M], Next[M], d[N];
bool v[N];
int n, m, tot;
// 大根堆(优先队列),pair的第二维为节点编号
// pair的第一维为dist的相反数(利用相反数变成小根堆,参见0x71节)
priority_queue< pair<int, int> > q;
void add(int x, int y, int z) {
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
void dijkstra() {
memset(d, 0x3f, sizeof(d)); // dist数组
memset(v, 0, sizeof(v)); // 节点标记
d[1] = 0;
q.push(make_pair(0, 1));
while (q.size()) {
// 取出堆顶
int x = q.top().second; q.pop();
if (v[x]) continue; //每个节点只会访问一次
v[x] = 1;
// 扫描所有出边
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i], z = edge[i];
if (d[y] > d[x] + z) {
// 更新,把新的二元组插入堆
d[y] = d[x] + z;
q.push(make_pair(-d[y], y));
}
}
}
}
2.Bellman-Ford
点击查看代码
const int INF = 0x3f3f3f3f;
struct edge{int from, to, cost;};
edge es[MAX_E]; //边
int d[MAX_V]; //最短距离
int V, E;
void shortest_path(int s) {
for(int i = 0; i < V; i++) d[i] = INF;
d[s] = 0;
while (true) {
bool update = false;
for(int i = 0; i < E; i++) {
edge e = es[i];
if(d[e.from] != INF && d[e.to] > d[e.from] + e.cost) {
d[e.to] = d[e.from] + e.cost;
update = true;
}
}
if(!update) break;
}
}
判断是否存在负圈
点击查看代码
const int INF = 0x3f3f3f3f;
struct edge{int from, to, cost;};
edge es[MAX_E]; //边
int d[MAX_V]; //最短距离
int V, E;
bool find_negative_loop() {
memset(d, 0, sizeof(d));
for(int i = 0; i < V; i++) {
for(int j = 0; j < E; j++) {
edge e = es[j];
if(d[e.to] > d[e.from] + e.cost) {
d[e.to] = d[e.from] + e.cost;
//存在负圈
if(i == V - 1) return true;
}
}
}
return false;
}
3.SPFA
点击查看代码
const int N = 100010, M = 1000010;
int head[N], ver[M], edge[M], Next[M], d[N];
int n, m, tot;
queue<int> q;
bool v[N];
void add(int x, int y, int z) {
ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
void spfa() {
memset(d, 0x3f, sizeof(d)); // dist数组
memset(v, 0, sizeof(v)); // 是否在队列中
d[1] = 0; v[1] = 1;
q.push(1);
while (q.size()) {
// 取出队头
int x = q.front(); q.pop();
v[x] = 0;
// 扫描所有出边
for (int i = head[x]; i; i = Next[i]) {
int y = ver[i], z = edge[i];
if (d[y] > d[x] + z) {
// 更新,把新的二元组插入堆
d[y] = d[x] + z;
if (!v[y]) q.push(y), v[y] = 1;
}
}
}
}
2.多源最短路
1.Floyd-Warshall
点击查看代码
int d[MAX_V][MAX_V]; //d[u][v]表示边e=(u,v)的权值 不存在时设为INF d[i][i] = 0;
int V;
void warshall_floyd() {
for(int k = 0; k < V; k++) {
for(int i = 0; i < V; i++) {
for(int j = 0; j < V; j++) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
传递闭包问题
若 a X b 且 b X c则 a X c
点击查看代码
bool d[310][310];
int n, m;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) d[i][i] = 1;
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
d[x][y] = d[y][x] = 1;
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] |= d[i][k] & d[k][j];
}