核心代码:
//d[i][j]表示节点i到j的最短路 for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) d[i][j] = min(d[i][j] , d[i][k]+d[k][j]); 其中k一定是在最外面,理解为什么在最外面是最难,这里利用了动态规划的思想。 理解时可以扩大维度 f[k][i][j]表示i->j经过k的最短路,f[k-1][i][j]表示经过k-1,的最短路 f[k][i][j] = min{f[k-1][i][k],f[k-1][i][k] + f[k-1][k][j]} f[k][i][j] 由公式是有f[k-1][i][j] 转化而来,所以k是阶段。1. 初始队列和标记数组 2. 源点入队。 3. 对队首点出发的所有边进行松弛操作(即更新最小值)。 4. 将不在队列中的尾结点入队。 5. 队首点更新完其所有的边后出队。
queue<int> q; bool inqueue[N]; // 是否在队列中 int cnt[N]; // 检查负环时使用:结点进队次数。如果超过n说明有负环。 bool SPFA(int start) { // 有负环则返回false // 初始队列和标记数组 for (int i=0; i<n; i++) d[i]=INF; d[start]=0; memset(cnt,0,sizeof(cnt)); q.push(start); // 源点入队 cnt[start]++; while (!q.empty()) { int x=q.front(); q.pop(); inqueue[x]=false; // 对队首点出发的所有边进行松弛操作(即更新最小值) for (edge *e=adj[x];e!=NULL;e=e->next){ int &v=e->v, &w=e->w; if (d[v]>d[x]+w) { d[v] = d[x]+w; // 将不在队列中的尾结点入队 if (!inqueue[v]) { inqueue[v]=true; q.push(v); if (++cnt[v]>n) return false; // 有负环 } } } } return true; }