P5960 【模板】差分约束 题解
P5960 【模板】差分约束
Description
给你一个差分约束系统,让你判断这个差分约束系统是否存在合法的解,如果存在,输出任意一组可行解。
\(1\le n,m \le 5\times 10^3\)
Solution
我们单独把一个式子拿出来看:
\[x_i-x_j\le c_k
\]
移项:
\[x_i\le x_j+c_k
\]
瞬间想起了一些不好的东西。
int v=e[i].to,w=e[i].w;
if(dis[v]>=dis[u]+w){
dis[v]=dis[u]+w;
}
对没错这个东西很像最短路中的三角形不等式:\(\text{dis[v]}\le \text{dis[u]+w}\)。
考虑把三角形不等式稍作改动:\(\text{dis[i]}\le \text{dis[j]+k}\)。最终的最短路径中每条边都要满足这个不等式,而差分约束的最终可行解每个 \(x_i\) 也都要满足这个不等式。
因此,对于每一个不等式,我们考虑建一条从 \(j\) 到 \(i\),长度为 \(c_k\) 的有向边。
我们先建立一个超级源点,不妨设为 \(0\) 号点。从 \(0\) 号点向所有点连一条长度为 \(0\) 的边,跑最短路即可。
(Tips:差分约束的式子也可以拿来跑最长路)
我们不清楚图的具体形态,所以要注意判断负环。当存在负环时(大多数时候使用 SPFA 来做),差分约束系统无解。
最短路结束之后,若差分约束系统有可行解,一组可行解即为 \(x_i=dis_i\)。
复杂度就是 SPFA 的复杂度,最坏 \(O(nm)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
long long n,m,tot,head[10005],dis[10005],cnt[10005];
bool vis[10005];
struct node{
int from,to,w,nxt;
}e[20005];
inline void add_edge(int u,int v,int w){
e[++tot].from=u;
e[tot].to=v;
e[tot].w=w;
e[tot].nxt=head[u];
head[u]=tot;
return;
}
queue<int>q;
inline bool SPFA(){
vis[0]=1;
q.push(0);
memset(dis,63,sizeof(dis));
dis[0]=0;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=e[i].nxt){
int u=x,v=e[i].to,w=e[i].w;
if(dis[v]>=dis[u]+w){
dis[v]=dis[u]+w;
if(!vis[v]){
cnt[v]++;
vis[v]=1;
if(cnt[v]>n){
return 1;
}
q.push(v);
}
}
}
}
return 0;
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
add_edge(0,i,0);
}
for(int i=1;i<=m;i++){
int x,y,k;
cin>>x>>y>>k;//x-y<=k -> x<=y+k
add_edge(y,x,k);
}
if(SPFA()){
cout<<"NO"<<endl;
}
else{
for(int i=1;i<=n;i++){
cout<<dis[i]<<" ";
}
}
return 0;
}

浙公网安备 33010602011771号