洛谷 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;
}