【模板】差分约束算法

题目描述
给出一组包含 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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值