前置芝士
👉SPFA单源最短路
定义
来自OI-Wiki blue
差分约束系统 是一种特殊的 $n$ 元一次不等式组,它包含 $n$ 个变量 $x_1,x_2,\dots,x_n$ 以及 $m$ 个约束条件,每个约束条件是由两个其中的变量做差构成的,形如 $x_i-x_j\leq c_k$,其中 $1 \leq i, j \leq n, i \neq j, 1 \leq k \leq m$ 并且 $c_k$ 是常数(可以是非负数,也可以是负数)。我们要解决的问题是:求一组解 $x_1=a_1,x_2=a_2,\dots,x_n=a_n$,使得所有的约束条件得到满足,否则判断出无解。
思路
我们将每个约束条件$x_i-x_j\leq c_k$变形成$x_i\leq x_j+c_k$,然后这个形式很像单源最短路中的 $dist[y]\leq dist[x]+z$ 。因此,我们可以把每个变量 $x_i$ 看做图中的一个结点,对于每个约束条件 $x_i-x_j\leq c_k$,从结点 $\color{red}{j}$ 向结点 $\color{red}{i}$ 连一条长度为 $c_k$ 的有向边。
- 若图不是联通的,我们可以设 $dist[0]=0$ 并向每一个点连一条权重为 $0$ 的边。
- 跑单源最短路若图中存在负环,则给定的差分约束系统无解,否则,$x_i=dist[i]$ 为该差分约束系统的一组解。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include<iostream> #include<vector> #include<queue> #include<cstring> using namespace std; inline int read(){ int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar())if(c=='-')f=-1; for(;isdigit(c);c=getchar())x=(x<<3)+(x<<1)+(c^48); return x*f; } int n,m; struct node{ int to; int val; }; vector<node>ro[100005]; queue<int>Q; int dis[100005],cnt[100005]; bool vis[100005]; bool spfa(){ memset(dis,0x3f3f3f3f,sizeof dis); dis[0]=0,vis[0]=true;Q.push(0); while(Q.size()){ int now = Q.front();Q.pop();vis[now] = false; for(node to:ro[now]){ if(dis[to.to]>dis[now]+to.val){ dis[to.to]=dis[now]+to.val; cnt[to.to]=cnt[now]+1; if(cnt[to.to]>=n+1)return false; if(!vis[to.to])Q.push(to.to),vis[to.to]=true; } } } return true; } int main(){ n=read(),m=read(); for(int i = 1,u,v,w;i<=m;i++){ u=read(),v=read(),w=read(); ro[v].push_back((node){u,w}); } for(int i = 1;i<=n;i++){ ro[0].push_back((node){i,0}); } bool NO_NG_CL = spfa(); if(!NO_NG_CL)cout<<"NO\n"; else{ for(int i = 1;i<=n;i++){ cout<<dis[i]<<" "; } } return 0; }
|