spfa

· · 题解

\text{spfa}

--队列优化的\text{bellman-ford}算法

观察$\text{bellman-ford}$的运行(从我bf算法的博客摘来的): ![1](https://cdn.luogu.com.cn/upload/image_hosting/13n2qds1.png) ![2](https://cdn.luogu.com.cn/upload/image_hosting/3q0urgnd.png) ![3](https://cdn.luogu.com.cn/upload/image_hosting/vj61p32p.png) ![4](https://cdn.luogu.com.cn/upload/image_hosting/1vdhugzy.png) 发现有些点,你从前$i$个点根本松弛不到,于是我们采用广搜的方式(即用队列)对图进行遍历,避免时间的浪费,但是普通的广搜在寻找最短路的时候无法保证当前的节点是最优的,看张图: ![bfs-wrong](https://cdn.luogu.com.cn/upload/image_hosting/7ok8n82u.png) 要是之前的某个点不是最优的,后边搜到的点也无法保证最优 于是我们的$\text{spfa}$算法非常聪明地避免了这一情况,将需要更新的节点重新入队(如果它不在队列中),以保证后边搜到的点的权值最优(~~大不了重新搜一遍后边的点吗~~) 以上边的图为例,在点3松弛完点2时,重新入队点2,于是 ![right](https://cdn.luogu.com.cn/upload/image_hosting/4p4fx51g.png) 但也是这条给了毒瘤出题人卡$\text{spfa}$算法的空间,即让元素一遍一遍地重新入队,已达到时间退化的效果 综上,$\text{spfa}=\text{bfs}(\text{queue})+\text{bellman-ford}

\text{spfa}判负环(咕咕咕咕

code:\text{spfa}模板

#include <iostream>
#include <fstream>
#include <queue>
#include <cstdlib>

using namespace std;

const int MAXN = 5e5 + 5;

int head[MAXN], next[MAXN], to[MAXN], weight[MAXN];
int tot = 0;

int n, m, s;
int dis[MAXN], vis[MAXN];
bool inQueue[MAXN];
queue<int> q;

void AddEdge(int u, int v, int w) {
  next[++tot] = head[u];
  to[tot] = v;
  weight[tot] = w;
  head[u] = tot;
}

bool Relax(int u, int v, int w) {
  if(dis[v] > dis[u] + w) {
    dis[v] = dis[u] + w;
    return true;
  }
  return false;
}

void Error() {
  cout << "negetive loop!" << endl;
  exit(0);
}

void Spfa(int start) {
  for(int i = 0; i < MAXN; i++) {
    dis[i] = 2147483647;
  }
  q.push(start);
  inQueue[start] = true;
  dis[start] = 0;
  vis[start] = 1;
  while(!q.empty()) {
    int u = q.front();
    q.pop();
    vis[u]++;
    inQueue[u] = false;
    if(vis[u] > n) { //判负环 
      Error();
    }
    for(int i = head[u]; i; i = next[i]) {
      int v = to[i];
      int w = weight[i];
      if(Relax(u, v, w) && !inQueue[v]) {
    q.push(v);
        inQueue[v] = false;
      }
    }
  }
}

int main() {
  ifstream inFile("spfa.in");
  cin >> n >> m >> s;
  for(int i = 0; i < m; i++) {
    int u, v, w;
    cin >> u >> v >> w;
    AddEdge(u, v, w);
  }
  Spfa(s);
  for(int i = 1; i < n + 1; i++) {
    cout << dis[i] << ' ';
  }
  cout << endl;
  return 0;
}