SPFA
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\)是否在队列中。
- \(\zeta_{\{1\dots n\}} = \infty\)
- \(\zeta_\pi = 0\)
- 新建一个空队列\(\phi\)
- 往\(\phi\)里加\(\pi\),\(\kappa_\pi = 1\)
- while \(\phi\)非空
- \(\lambda\) = pop \(\phi\),\(\kappa_\lambda = 0\)
- for \(\delta \in \lambda的邻居\)
- if $ \zeta_\delta > \zeta_\lambda + \xi_{\lambda,\delta}$
- \(\zeta_\delta = \zeta_\lambda + \xi_{\lambda,\delta}\)
- if \(\lnot \kappa_\delta\)
- 往\(\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的更多相关文章
- 【BZOJ-3627】路径规划 分层图 + Dijkstra + spfa
3627: [JLOI2014]路径规划 Time Limit: 30 Sec Memory Limit: 128 MBSubmit: 186 Solved: 70[Submit][Status] ...
- POJ 2387 Til the Cows Come Home(最短路 Dijkstra/spfa)
传送门 Til the Cows Come Home Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 46727 Acce ...
- sgu 240 Runaway (spfa)
题意:N点M边的无向图,边上有线性不下降的温度,给固定入口S,有E个出口.逃出去,使最大承受温度最小.输出该温度,若该温度超过H,输出-1. 羞涩的题意 显然N*H的复杂度dp[n][h]表示到达n最 ...
- spfa模板
通过stl的queue实现的spfa(vector实现邻接表存图) 本模板没有考虑存在两点不连通的情况 如果需要判断则需要用到并查集或者遍历整个邻接表 #include<iostream> ...
- BZOJ2763 [JLOI2011]飞行路线(SPFA + DP)
题目 Source http://www.lydsy.com/JudgeOnline/problem.php?id=2763 Description Alice和Bob现在要乘飞机旅行,他们选择了一家 ...
- bzoj3380: [Usaco2004 Open]Cave Cows 1 洞穴里的牛之一(spfa+状压DP)
数据最多14个有宝藏的地方,所以可以想到用状压dp 可以先预处理出每个i到j的路径中最小权值的最大值dis[i][j] 本来想用Floyd写,无奈太弱调不出来..后来改用spfa 然后进行dp,这基本 ...
- bzoj 1179[Apio2009]Atm (tarjan+spfa)
题目 输入 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一 ...
- codevs 1021 玛丽卡(spfa)
题目描述 Description 麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复. 因为她和他们不住在同一个城市,因此她开始准备她的长途旅行. 在这个国家中每两个城市之间最多只有一条路相通,并且我们 ...
- UVA11090 Going in Cycle!! [spfa负环]
https://vjudge.net/problem/UVA-11090 平均权值最小的回路 为后面的做个铺垫 二分最小值,每条边权减去他,有负环说明有的回路平均权值小于他 spfa求负环的时候可以先 ...
随机推荐
- CSS3 速移动效果动画流畅无卡顿
js或jquery 元素移动以像素计算,手机上移动效果会有卡顿 利用CSS3 可以很简单的实现流畅的移动动画 transform: translate3d(66px, 88px, 0px) rotat ...
- tomcat配置
修改可用内存大小 D:\escloud\apache-tomcat-7.0.63\bin 下修改catalina.bat set "JAVA_OPTS=-Xms1024m -Xmx1024m ...
- [Algorithm & NLP] 文本深度表示模型——word2vec&doc2vec词向量模型
深度学习掀开了机器学习的新篇章,目前深度学习应用于图像和语音已经产生了突破性的研究进展.深度学习一直被人们推崇为一种类似于人脑结构的人工智能算法,那为什么深度学习在语义分析领域仍然没有实质性的进展呢? ...
- [Algorithm] 机器学习算法常用指标总结
考虑一个二分问题,即将实例分成正类(positive)或负类(negative).对一个二分问题来说,会出现四种情况.如果一个实例是正类并且也被 预测成正类,即为真正类(True positive), ...
- GOPATH 使用总结
GOPATH 环境变量用于指定这样一些目录:除 $GOROOT 之外的包含 Go 项目源代码和二进制文件的目录.go install 和 go 工具会用到 GOPATH:作为编译后二进制的存放目的地 ...
- Oracle的SQL语句中的变量替换
一.问题描述 如下SQL: INSERT INTO tmp(val)VALUES('a&b'); 执行过程中会出现如下提示: 点击"确定"过后我们查看表中的数据: b后面的 ...
- HTML页面如何判断是手机访问还是电脑访问
可以通过js来判断访问设备,代码如下: <script type="text/javascript"> var system ={}; var p = navigato ...
- Angular2 依赖注入
1. 使用DI 依赖注入是一个很重要的程序设计模式. Angular 有自己的依赖注入框架,离开了它,我们几乎没法构建 Angular 应用.它使用得非常广泛,以至于几乎每个人都会把它简称为 DI. ...
- 让“是男人就下到100层”在Android平台上跑起来
原工程:https://github.com/jeekun/DownFloors 移植后的代码:HelloCpp.zip 移植后的APK:HelloCpp.apk 说明:(cocos2d-x版本是“ ...
- cocos2d-x for android配置 & 运行 Sample on Linux OS
1.从http://www.cocos2d-x.org/download下载稳定版 比如cocos2d-x-2.2 2.解压cocos2d-x-2.2.zip,比如本文将其解压到 /opt 目录下 3 ...