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求负环的时候可以先 ...
随机推荐
- kettle将Excel数据导入oracle
导读 Excel数据导入Oracle数据库的方法: 1.使用PL SQL 工具附带的功能,效率比较低 可参考这篇文章的介绍:http://www.2cto.com/database/201212/17 ...
- 前端面霸系列(1):doctype 、Quirks Mode & Standards Mode 、document.compatMode
近几日,气压猛降,雾霾铺天盖地,眼看一场腥风血雨就要在前端江湖爆发,这场战争不仅是百度.腾讯.阿狸.搜狐网易新浪等江湖豪门抢夺人才的大战,也是诸位江湖人士重新洗牌的好时机.每年10月,江湖的波动胜过华 ...
- 弹框控件 UIAlertView UIActionSheet
// 创建弹框 从底部弹出,一般用于危险操作 UIActionSheet *sheet = [[UIActionSheet alloc] initWithTitle:@"恭喜通关" ...
- Jetty+Xfire 嵌入式webService应用实践
1:使用场景:Mock公安网证件信息校验 2:Jetty嵌入式Server启动方式:由于Jetty9.x(需jdk7.x以上)以后Server启动方式有略微差异,所以分开说明: 2.1 Jetty9. ...
- 使用axis开发web service服务端
一.axis环境搭建 1.安装环境 JDK.Tomcat或Resin.eclipse等. 2.到 http://www.apache.org/dyn/closer.cgi/ws/axis/1_4下载A ...
- Android基础 : Android ContentProvider
Android 应用程序通过ContentProvider实现方式统一的数据共享功能. 外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activi ...
- Autoit中用PrintWindow替代ScreenCapture函数实现截图
想截取躲在后面的窗体或控件,找到了PrintWindow函数,幸运的是Autoit3也对此进行了封装以方便使用. 于是乎,将帮助文件里的_WinAPI_PrintWindow()实例改写了一下,以替代 ...
- c++ map、vector、list
总体来说,使用map最简单.支持查找,获取下标不存在也不会出错 map是使用rbtree结构, vector是用连续获取内存的方法,类似hash结构.list是链表结构, 不支持下标. map: 支持 ...
- javascript语言理解
1.使用jquery remove,无法remove自身标签; 使用标签
- 利用box-shadow实现伪边框透明到图片
前阵子突然看到了一个效果,一张图片,有一个边框,但是边框可以透明到图片.直接上图 贴代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...