#蒟蒻复习之-----SPFA,dijkstra#
//好把这个太简单了
//作为复习篇不讲原理
##dijkstra##
//表示很少用,即使他要比spfa稳定
附赠代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 500000 + 100;
const int inf = 2147483647;
int n,m,root;
struct edge {
int u,v,w;
int next;
}e[maxn];
int head[maxn], tot = 0;
struct node {
int x,val;
bool operator < (node a) const{
return val > a.val;
}
};
priority_queue<node>q;
void add(int u, int v, int w) {
e[++tot] = (edge){u,v,w,head[u]};
head[u] = tot;
}
int vis[maxn],d[maxn];
void dijkstra(int x) {
memset(vis,0,sizeof(vis));
for(int i = 1; i <= n; i++) d[i] = inf;
d[x] = 0;
q.push((node){x,0});
while(!q.empty()) {
int k = q.top().x;
q.pop();
if(vis[k]) continue;
vis[k] = 1;
for(int i = head[k]; i; i = e[i].next) {
int v = e[i].v;
if(d[v] > d[k] + e[i].w) {
d[v] = d[k] + e[i].w;
q.push((node){v,d[v]});
}
}
}
}
int main() {
cin>>n>>m>>root;
for(int i = 1; i <= m; i++) {
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
}
dijkstra(root);
for(int i = 1; i <= n; i++) {
cout<<d[i]<<' ';
}
return 0;
}
##SPFA##
//本萌新最喜欢的算法
//~~~~玄学
SPFA算法是1994年西安交通大学段凡丁提出 %%%%
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int maxn = 500000 + 100;
const int inf = 2147483647;
int n,m,root;
struct edge {
int u,v,w;
int next;
}e[maxn];
int head[maxn], tot = 0;
void add(int u, int v, int w) {
e[++tot] = (edge){u,v,w,head[u]};
head[u] = tot;
}
int vis[maxn],d[maxn];
void spfa(int x) {
memset(vis,0,sizeof(vis));
for(int i = 1; i <= n; i++) d[i] = inf;
vis[x] = 1, d[x] = 0;
queue<int>q;
q.push(x);
while(!q.empty()) {
int k = q.front();
q.pop();
vis[k] = 0;
for(int i = head[k]; i; i = e[i].next) {
int v = e[i].v;
if(d[v] > d[k] + e[i].w) {
d[v] = d[k] + e[i].w;
if(!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
}
int main() {
cin>>n>>m>>root;
for(int i = 1; i <= m; i++) {
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
}
spfa(root);
for(int i = 1; i <= n; i++) {
cout<<d[i]<<' ';
}
return 0;
}
#spfa的用途#
##1.找最短路##
在松弛的同时记录下夫节点
得到的是最短路树(QAQ没啥用)
while(!q.empty()) {
int k = q.front();
q.pop();
vis[k] = 0;
for(int i = head[k]; i; i = e[i].next) {
int v = e[i].v;
if(d[v] > d[k] + e[i].w) {
d[v] = d[k] + e[i].w;
f[v] = k; //hear
if(!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
//找父亲 输出u——>v的路径 如果想正输可以建个队列存起来
do {
cout<<v<<' ';
v = f[v];
}while(v != u);
cout<<u<<' ';
##2.找负环##
spfa最主要的用途之一
一般写dfs版
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 400000 + 100;
int T,n,m;
struct edge {
int u,v,w;
int next;
}e[maxn << 1];
int head[maxn],tot = 0;
int read() {
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + ch - '0';
ch = getchar();
}
return x * f;
}
void add(int u, int v, int w) {
e[++tot] = (edge) {u,v,w,head[u]};
head[u] = tot;
}
int dis[maxn],vis[maxn],col[maxn],flag;
void spfa(int x) {
if(flag) return;
vis[x] = 1;
for(int i = head[x]; i; i = e[i].next) {
int v = e[i].v;
if(dis[v] > dis[x] +e[i].w) {
dis[v] = dis[x] + e[i].w;
if(vis[v]) {
flag = 1;
return;
}
else {
spfa(v);
}
}
}
vis[x] = 0;
return;
}
int main() {
T = read();
while(T--) {
memset(head,0,sizeof(head));
memset(e,0,sizeof(e));
memset(vis,0,sizeof(vis));
memset(dis,0,sizeof(dis));
tot = 0, flag = 0;
n = read(), m = read();
for(int i = 1; i <= m; i++) {
int u = read(), v = read(), w = read();
add(u,v,w);
if(w >= 0) add(v,u,w);
}
for(int i = 1; i <= n; i++) {
if(!vis[i]) spfa(i);
}
if( flag ) {
puts("YE5");
}
else {
puts("N0");
}
}
return 0;
}
##3.差分约束系统##
差分约束是求解N元一次特殊不等式组的一种方法。
差分约束系统包含N个变量和M个约束条件,每个约束条件都是一个关于两个变量的一次不等式,每个不等式形如X[i]-X[j]<=A[k],其中1<=i,j<=N,1<=k<=M,X[i]、X[j]为变量,A[k]为常数。
引理:若{Xi}为差分约束系统的一组解,Δ为常数,那么{Xi+Δ}也是一组解。
差分约束系统中的每个不等式都与最短路中的三角形不等式 dist[v]<=dist[u]+edge(u,v) 形似。
把变量Xi看做有向图中的点i,对不等式X[i]-X[j]<=A[k]从j向i连一条有向边,边权为A[k]。建立一个源点,向每个点连一条有向边,边权为0。从源点出发求单源最短路,X[i]=dist[i]就是一组可行解。有负环说明无解。
若对X[i]有范围限制,可用X[i]与源点之间的约束表示。
不等式>=号时用最长路解决,此时有正环说明无解。
dwarf tower(SPFA变形)
伊吹萃香(建图)
道路和飞机Roads and Planes(SLF优化)
天路(二分+SPFA)
狡猾的商人(差分约束)