SPFA算法用来求单源最短路。可以处理任何有解的情况。

先建一个数组\(dist_x = 起点到x的最短路长度\),当\(x=起点\)时为0,当x和起点不通时为INF(本题中为\(2^31-1\))。

明确一下松弛的概念。考虑节点\(u\)以及它的邻居\(v\),从起点跑到v有好多跑法,有的跑法经过\(u\),有的不经过,那么经过\(u\)的跑法的距离就是\(dist_u + u到v的距离\)。所谓松弛操作,就是看一看\(dist_v\)和\(dist_u + u到v的距离\)哪个大一点,如果前者大一点,就说明当前的不是最短路,就要赋值为后者,这就叫做松弛。松弛,英文中是叫relax的,为什么叫这个名字我也不知道。

接着维护一个队列,保存待松弛的节点。一开始设起点的dist为0,然后把起点放进队列里。然后就开始循环,如果队空就说明全部节点都松完了,可以退出循环,没空就要继续松。把队列第一个元素拉出来,然后依次松弛它的邻居,对于每个邻居,如果松弛成功了,就看看那个邻居在不在队里,如果不在队里就把那个邻居扔进队尾。

下面\(\pi\)代表起点,\(\lambda\)表示某个节点,\(\delta\)表示邻居,\(\zeta_\lambda\)表示起点到\(\lambda\)的距离,\(\xi_{\lambda,\delta}\)表示\(\lambda到\delta的距离\),\(\kappa_\lambda\)表示\(\lambda\)是否在队列中。

  1. \(\zeta_{\{1\dots n\}} = \infty\)
  2. \(\zeta_\pi = 0\)
  3. 新建一个空队列\(\phi\)
  4. 往\(\phi\)里加\(\pi\),\(\kappa_\pi = 1\)
  5. while \(\phi\)非空
  6.   \(\lambda\) = pop \(\phi\),\(\kappa_\lambda = 0\)
  7.   for \(\delta \in \lambda的邻居\)
  8.     if $ \zeta_\delta > \zeta_\lambda + \xi_{\lambda,\delta}$
  9.       \(\zeta_\delta = \zeta_\lambda + \xi_{\lambda,\delta}\)
  10.       if \(\lnot \kappa_\delta\)
  11.         往\(\phi\)里加\(\delta\),\(\kappa_\delta = 0\)

别人的文章

原文 20160517

差分约束系统

一般来说,对于一类“两个未知数的差小于等于(或大于等于)某个常数” (xi−xj≤c)(xi−xj≤c) 不等式组的求解,便是差分约束系统。差分约束系统的美妙之处在于可以将其转为图论,通过求解单源最短路来判断原不等式组是否有解甚至求得最大或最小解。

首先,显然对于此类不等式组,要么无解,要么有无数解。因为假设存在一组解为 x1,x2,…,xnx1,x2,…,xn,那么对于任意一个常数 kk,x1+k,x2+k,…,xn+kx1+k,x2+k,…,xn+k 也必是一组解,因为他们的差值是不变的,所以不等式组依然成立。

接下来考虑单源最短路图,对于某点 vv 与其邻接点 uu,显然 dist[v]≤dist[u]+costu−>vdist[v]≤dist[u]+costu−>v,也即 dist[v]−dist[u]≤costu−>vdist[v]−dist[u]≤costu−>v,正好满足“两个未知数的差小于等于某个常数”。因此,我们将待求解不等式组中的未知数看成图上的顶点,对于每个不等式 xi−xj≤cxi−xj≤c,转化成图中顶点 ii 向 jj 建边,边权为 cc。

然后直接在建成的图上跑单源最短路就可以了。如果存在负环则无解,否则 dist[i]dist[i] 便是其中一组解,至于以哪个点作为源点都是可以的。相当于以哪个点作为源点就是将该点赋值为 00,通过该约束去得出不等式组中其他未知数的解。

还有一种是不仅需要判断是否有解,还要求求出一组最大/最小解,这种类型可以考虑通过增加一个点去对其他点进行约束,然后以这个点为源点跑最长/最短路求解,由于差分约束类型题目近几年几乎不可见了,所以基本没遇过这种类型,只能瞎BB下。

SPFA 算法

由于图中可能存在负环,所以不能使用 dijkstradijkstra 算法求解,可以考虑使用 SPFASPFA 或者 Bellman−FordBellman−Ford,个人建议用 SPFASPFA ,比较快。

最长路与最短路

SPFASPFA 求最长路的算法跟求最短路类似:

最长路是初始化为 00,当存在 dist[v]<dist[u]+costu−>vdist[v]<dist[u]+costu−>v 时进行更新。

最短路时初始化为 INFINF,当存在 dist[v]>dist[u]+costu−>vdist[v]>dist[u]+costu−>v 时进行更新。


P3371 【模板】单源最短路径

题目描述

如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。

输入输出格式

输入格式:

第一行包含三个整数N、M、S,分别表示点的个数、有向边的个数、出发点的编号。

接下来M行每行包含三个整数Fi、Gi、Wi,分别表示第i条有向边的出发点、目标点和长度。

输出格式:

一行,包含N个用空格分隔的整数,其中第i个整数表示从点S出发到点i的最短路径长度(若S=i则最短路径长度为0,若从点S无法到达点i,则最短路径长度为2147483647)

输入输出样例

输入样例#1:

4 6 1

1 2 2

2 3 2

2 4 1

1 3 5

3 4 3

1 4 4

输出样例#1:

0 2 4 3

说明

时空限制:1000ms,128M

数据规模:

对于20%的数据:N<=5,M<=15

对于40%的数据:N<=100,M<=10000

对于70%的数据:N<=1000,M<=100000

对于100%的数据:N<=10000,M<=500000

样例说明:


#include <algorithm>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector> namespace nr3371 { using std::fill;
using std::queue;
using std::vector; namespace io { inline int read(void) {
int x;
scanf("%d", &x);
return x;
} } // namespace io using io::read; const int errcode = 2147483647; inline void read_graph(void);
inline void spfa(void);
inline void print_result(void); inline void main(void) {
read_graph();
spfa();
print_result();
} //-------------------------------------------------- int point_cnt, edge_cnt, source; const int point_cnt_max = 1e4 + 100;
const int edge_cnt_max = 5e5 + 100; struct edge_t {
int dst;
long weight;
}; vector<edge_t> edges[point_cnt_max]; inline void read_graph(void) {
for (int i = 0; i < point_cnt; ++i) {
edges[i].clear();
} point_cnt = read();
edge_cnt = read();
source = read(); for (int i = 0; i < edge_cnt; ++i) {
int src = read();
edge_t e;
e.dst = read();
e.weight = read(); edges[src].push_back(e);
}
} // -------------------------------------- long dist[point_cnt_max]; inline void spfa(void) {
fill(dist, dist+point_cnt_max, errcode);
dist[source] = 0; vector<bool> vis(point_cnt+1);
fill(vis.begin(), vis.end(), false);
vis[source] = true; queue<int> q;
q.push(source); while (!q.empty()) {
int x = q.front();
q.pop();
vis[x] = false; for (vector<edge_t>::iterator it = edges[x].begin(); it != edges[x].end(); ++it) {
if (dist[it->dst] > dist[x] + it->weight) {
dist[it->dst] = dist[x] + it->weight; if (!vis[it->dst]) {
vis[it->dst] = true;
q.push(it->dst);
}
}
}
}
} // -------------------------------------- inline void print_result(void) {
for (int i = 1; i <= point_cnt; ++i) {
printf("%ld ", dist[i]);
} putchar('\n');
} } // namespace nr3371 int main(void) {
//freopen("in.txt", "r", stdin); nr3371::main();
return 0;
}

P2384 最短路

题目背景

狗哥做烂了最短路,突然机智的考了Bosh一道,没想到把Bosh考住了...你能帮Bosh解决吗?

他会给你100000000000000000000000000000000000%10金币w

题目描述

给定n个点的带权有向图,求从1到n的路径中边权之积最小的简单路径。

输入输出格式

输入格式:

第一行读入两个整数n,m,表示共n个点m条边。 接下来m行,每行三个正整数x,y,z,表示点x到点y有一条边权为z的边。

输出格式:

输出仅包括一行,记为所求路径的边权之积,由于答案可能很大,因此狗哥仁慈地让你输出它模9987的余数即可。

废话当然是一个数了w

//谢fyszzhouzj指正w

对于20%的数据,n<=10。

对于100%的数据,n<=1000,m<=1000000。边权不超过10000。

输入输出样例

输入样例#1:

3 3

1 2 3

2 3 3

1 3 10

输出样例#1:

9

说明

好好看一看再写哟w

题解

直接乘会爆,取对数把乘法改成加法即可,\(log_{ab} = log_a + log_b\),讨论区说测试样例太弱了,不log,甚至不取模也能过,所以也不知道有没有效。

从评论区学到了新知识,快速乘。另文。

#include <algorithm>
#include <cmath>
#include <iostream>
#include <queue>
#include <vector> using namespace std; int n, m; struct edge_t {
int dst;
int weight;
double logw;
}; vector<edge_t> edges[1005];
int weight[1005][1005]; int main(void) {
#ifdef DEBUG
freopen("in.txt", "r", stdin);
#endif cin >> n >> m; for (int i = 0; i < m; ++i) {
int src;
edge_t e;
cin >> src >> e.dst >> e.weight;
e.logw = log(e.weight);
weight[src][e.dst] = weight[e.dst][src] = e.weight; edges[src].push_back(e);
} queue<int> que;
que.push(1); vector<bool> vis(n+1);
fill(vis.begin(), vis.end(), false);
vis[1] = true; vector<double> dist(n+1);
fill(dist.begin(), dist.end(), 999999);
dist[1] = 0; vector<int> pre(n+1); while (!que.empty()) {
int x = que.front();
que.pop();
vis[x] = false; for (vector<edge_t>::iterator it = edges[x].begin(); it != edges[x].end(); ++it) {
if (dist[it->dst] > dist[x] + it->logw) {
dist[it->dst] = dist[x] + it->logw;
#ifdef DEBUG
cout << "[*] " << it->dst << endl;
#endif
pre[it->dst] = x; if (!vis[it->dst]) {
vis[it->dst] = true;
que.push(it->dst);
}
}
}
} int ans = 1;
for (int x = n; pre[x]; x = pre[x]) {
ans *= weight[x][pre[x]];
ans %= 9987;
} cout << ans;
}

这次代码比上一题要工好多。越晚越精神?

SPFA的更多相关文章

  1. 【BZOJ-3627】路径规划 分层图 + Dijkstra + spfa

    3627: [JLOI2014]路径规划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 186  Solved: 70[Submit][Status] ...

  2. POJ 2387 Til the Cows Come Home(最短路 Dijkstra/spfa)

    传送门 Til the Cows Come Home Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 46727   Acce ...

  3. sgu 240 Runaway (spfa)

    题意:N点M边的无向图,边上有线性不下降的温度,给固定入口S,有E个出口.逃出去,使最大承受温度最小.输出该温度,若该温度超过H,输出-1. 羞涩的题意 显然N*H的复杂度dp[n][h]表示到达n最 ...

  4. spfa模板

    通过stl的queue实现的spfa(vector实现邻接表存图) 本模板没有考虑存在两点不连通的情况 如果需要判断则需要用到并查集或者遍历整个邻接表 #include<iostream> ...

  5. BZOJ2763 [JLOI2011]飞行路线(SPFA + DP)

    题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=2763 Description Alice和Bob现在要乘飞机旅行,他们选择了一家 ...

  6. bzoj3380: [Usaco2004 Open]Cave Cows 1 洞穴里的牛之一(spfa+状压DP)

    数据最多14个有宝藏的地方,所以可以想到用状压dp 可以先预处理出每个i到j的路径中最小权值的最大值dis[i][j] 本来想用Floyd写,无奈太弱调不出来..后来改用spfa 然后进行dp,这基本 ...

  7. bzoj 1179[Apio2009]Atm (tarjan+spfa)

    题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...

  8. codevs 1021 玛丽卡(spfa)

    题目描述 Description 麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复. 因为她和他们不住在同一个城市,因此她开始准备她的长途旅行. 在这个国家中每两个城市之间最多只有一条路相通,并且我们 ...

  9. UVA11090 Going in Cycle!! [spfa负环]

    https://vjudge.net/problem/UVA-11090 平均权值最小的回路 为后面的做个铺垫 二分最小值,每条边权减去他,有负环说明有的回路平均权值小于他 spfa求负环的时候可以先 ...

随机推荐

  1. MongoDB【第二篇】MongoDB逻辑与物理存储结构

    基本的操作 一.常用的命令和基础知识 1.进入MongoDB sehll 首先我们进入到MongoDB所在目录执行 cd /work/app/mongodb/bin/ #启动 ./mongo 为了方便 ...

  2. Python之路【第二十四篇】Python算法排序一

    什么是算法 1.什么是算法 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出.简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果. ...

  3. metaWeblog Test

     #include "stdio.h" int main(int argc ,char **argv) { printf("argc = %d \n",arg ...

  4. Bubble Cup 8 finals A. Fibonotci (575A)

    题意: 定义类循环序列为 长度无限,且除了有限个元素外,均满足s[i] ≡ s[i mod N] (i≥N). 现在有数列F,定义为 F[i] = s[i-2]*F[i-1] + s[i-1]*F[i ...

  5. [UML]UML系列——时序图(顺序图)sequence diagram

    系列文章 [UML]UML系列——用例图Use Case [UML]UML系列——用例图中的各种关系(include.extend) [UML]UML系列——类图Class [UML]UML系列——类 ...

  6. oracle服务起不来以及无法监听问题解决

    改问题是在搭建起一个很早之前的数据库的时候碰见的,虽然这个问题网上已经有很多相关的帖子,但因最近碰见多次这样的问题,特此简单记录: 1.最开始碰见的问题是:The listener supports ...

  7. python小知识积累

  8. Ajax中get请求和post请求

    我们在使用Ajax向服务器发送数据时,可以采用Get方式请求服务器,也可以使用Post方式请求服务器,那么什么时候该采用Get方式,什么时候该采用Post方式呢? Get请求和Post请求的区别: 1 ...

  9. Java transient 关键字

    1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问. 2)transient关键字只能修饰变量,而不能修饰方法和类.注意,本地变量是不能被trans ...

  10. ComboBox数据的绑定

    //带有ComboBox控件点Load事件 private void Library_Load(object sender, EventArgs e) { //创建连接字符串 string strCo ...