洛谷 P5960 【模板】差分约束算法(spfa)

传送门


解题思路

给你\(n\)个不等式,\(ai<=aj+k\)
像极了最短路中松弛的式子
所以做法就出来了:
对于每一个是上面形式的式子,我们从\(j\)\(i\)连一条长度为\(k\)的边,这样我们保证了到\(i\)的最短路一定\(<=\)\(j\)的最短路。
建立一个超级源点,连向每一个点,边权为\(0\),求一遍最短路spfa,每个点到超级源点的最短路长度即为值的大小,而刚刚边权\(0\)其实就是确定了最终解的上界大小,即解都\(<=0\)
注意有负环则无解(用spfa可判断)。

update 2021/9/28:

再做差分约束的题又不理解了。
再做一下总结与补充:
按照小于等于建边的时候,因为
\(\begin{cases} a-b\leqslant c \\ a-b\leqslant d \\ \end{cases} \Rightarrow a-b\leqslant\min(c,d)\)
所以应该跑最短路,最终结果为a-b的上界(最大值)。
按照大于等于同理,跑最长路,求出来的是a-b的下界(最小值)。

AC代码

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=5005;
int n,m,p[maxn],dis[maxn],cnt,vis[maxn],times[maxn];
struct node{
	int v,next,w;
}e[maxn*2];
void insert(int u,int v,int w){
	cnt++;
	e[cnt].v=v;
	e[cnt].w=w;
	e[cnt].next=p[u];
	p[u]=cnt;
}
queue<int> q;
bool spfa(){
	memset(dis,0x3f,sizeof(dis));
	dis[0]=0;
	q.push(0);
	vis[0]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		times[u]++;
		if(times[u]>n) return false;
		for(int i=p[u];i!=-1;i=e[i].next){
			int v=e[i].v;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	return true;
}
int main()
{
	memset(p,-1,sizeof(p));
    cin>>n>>m;
    for(int i=1;i<=m;i++){
    	int u,v,w;
    	scanf("%d%d%d",&v,&u,&w);
    	insert(u,v,w);
	}
	for(int i=1;i<=n;i++) insert(0,i,0);
	if(!spfa()){
		cout<<"NO"<<endl;
		return 0;
	}
	for(int i=1;i<=n;i++) cout<<dis[i]<<' ';
    return 0;
}
posted @ 2021-05-19 00:24  尹昱钦  阅读(94)  评论(0编辑  收藏  举报