题目描述
给出一组包含 m个不等式,有 n 个未知数的形如:
x1-x1'<=y1 x2-x2'<=y2 x3-x3'<=y3... xm-xm'<=ym
输入格式
第一行为两个正整数 n,mn,m,代表未知数的数量和不等式的数量。
接下来 m 行,每行包含三个整数 c,c’,y
代表一个不等式 xc-xc’ ≤y。
输出格式
一行,n 个数,表示 x1 , x2 … xn 的一组可行解,如果有多组解,请输出任意一组,无解请输出 NO。
输入输出样例
输入
3 3 1 2 3 2 3 -2 1 3 1
输出
5 3 5
差分约束模板题
差分约束问题可以转化为最短路或最长路问题
1/连边后求最短路
将xj-xi<=k变形成xj<=xk’ 即在i到j连一条边权为k的边。加入超级原点求最短路,xi<=0所有x的最大解。
2/连边后求最长路
将xj-xi<=k变形成xi>=xk’ 即在j到i连一条边权为-k的边。加入超级原点求最长路,xi>=0所有x的最小解。
负环/正环判断
那么,如果万一用最短路求时出现负环,或用最长路时出现正环怎么办?
注:本段只考虑求最短路时的情况,最长路实质与最短路类似。
此时,咱们先不考虑怎么解决,先看这时的不等式组是什么样子的。
图存在负环,如果一直沿着负环走,最短路径将会越来越小,最后到达-∞。图中存在负环,则该不等式组无解。
此时,即可放心大胆地 SPFA,只需在 SPFA 的同时用一个数组来记录每个顶点入队次数,如果一个顶点入队次数大于 n,说明该图存在负环。
区别于最短路的最长路问题
最长路问题即为在给定的图中,计算从源点到所有顶点的最长路。保证图中没有正环。
其中一种实现方法为若 du+w>dv,则将dv
更新为 du+wd 实际上就是把最短路中的大于号改成小于号),并在初始化时将 d 数组全部初始化为一个极小值,其余部分和用 SPFA 求最短路一样。
注 spfa 从0开始 才可以正确判断是否负环
但 如果在求最短路中 正权边会更新成0 ,在最长路中,负权边会更新成0;(加入的超级源点到各点距离为0) 这个题只是求方程的解,从0开始问题不大。但在实际应用中,往往是求最大值,或者最小值,需要具体分析。如排队布局求最大值,从0开始,答案明显不对。需要从1开始再算一次。
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int n,m,s;
int h[N],idx,ne[N],e[N],w[N];
void add(int a,int b,int c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int dist[N],st[N],cnt[N];
int spfa(){
memset(dist,-1,sizeof dist);
queue<int> q;
q.push(0),st[0]=1,dist[0]=0;
while(q.size()){
int t=q.front();q.pop();st[t]=0;
for(int i=h[t];i!=-1;i=ne[i]){
int j=e[i];
if(dist[j]<dist[t]+w[i]){
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>n+1) return -1;//判断负环,因为加了一个超级源点,故应跟 n + 1 而不是 n 比较。
if(st[j]!=1) st[j]=1,q.push(j);
}
}
}
return 0;
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=m;i++){
int a,b,c;cin>>a>>b>>c;
add(a,b,-c);
}
for(int i=1;i<=n;i++) add(0,i,0);//加入超级原点 到所有边距离为0(这个距离可以随便设,只要所有点到原点距离相同就行) 但是不要太离谱
int t=spfa();
if(t==-1) cout<<"NO";
else {
for(int i=1;i<=n;i++) cout<<dist[i]<<" ";
}
return 0;
}