差分约束就是:给你 n 个变量,m 个未知数,形成这样几组的不等式: x i x_i xi - y j y_j yj <= c k c_k ck;
然后让你求出一组解,使得所有约束条件都满足;
怎么解呢?可以把上面的式子变形一下: x i x_i xi <= y j y_j yj + c k c_k ck;
这是不是和图论最短路的式子非常相似呢?
所以可以连接 y i y_i yi 到 x i x_i xi 的一条单向边,权值为 c k c_k ck ,然后求单源最短路即可,但是还要保证图的连通,所以可以加个超级源点,用 0 连接每个点,建立一条权值为 0 的边;
还有一种情况就是无解情况,什么时候会无解呢?
就是出现负环的情况,所以还要判断是否有负环,那么dij肯定是不可以了,spfa就成了最好的选择;
扩展:
如果是求 x i x_i xi<=0 的最大解,那么 y i y_i yi 到 x i x_i xi 连边,跑最短路;
如果是求 x i x_i xi>0 的最小解,那么 x i x_i xi 到 y i y_i yi 连边,跑最长路,这个和普通的最长路还不太一样,具体看代码;
模板题:洛谷·P5960 【模板】差分约束算法
第一种代码:
#include
#define LL long long
#define pa pair
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=50100;
const int mod=1e9;
int n,m;
struct Node{
int to,nex,w;
}edge[N*2];
int head[N],cnt,dis[N],vis[N],sum[N];
void add(int p,int q,int w){
edge[cnt].w=w,edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;
}
bool spfa(){
for(int i=1;i<=n;i++) dis[i]=2e9;
queue<int>qu;qu.push(0);
while(!qu.empty()){
int p=qu.front();qu.pop();
vis[p]=0;sum[p]++;
if(sum[p]>=n) return false;
for(int i=head[p];~i;i=edge[i].nex){
int q=edge[i].to;
if(dis[q]>dis[p]+edge[i].w){//最短路
dis[q]=dis[p]+edge[i].w;
if(!vis[q]) qu.push(q),vis[q]=1;
}
}
}
return true;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) add(0,i,0);
for(int i=1;i<=m;i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
add(b,a,c);
}
if(!spfa()) printf("NO\n");
else for(int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}
第二种代码:
#include
#define LL long long
#define pa pair
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=50100;
const int mod=1e9;
int n,m;
struct Node{
int to,nex,w;
}edge[N*2];
int head[N],cnt,dis[N],vis[N],sum[N];
void add(int p,int q,int w){
edge[cnt].w=w,edge[cnt].to=q,edge[cnt].nex=head[p],head[p]=cnt++;
}
bool spfa(){
for(int i=1;i<=n;i++) dis[i]=-1;
queue<int>qu;qu.push(0);
while(!qu.empty()){
int p=qu.front();qu.pop();
vis[p]=0;sum[p]++;
if(sum[p]>=n) return false;
for(int i=head[p];~i;i=edge[i].nex){
int q=edge[i].to;
if(dis[q]<dis[p]+edge[i].w){//最长路
dis[q]=dis[p]+edge[i].w;
if(!vis[q]) qu.push(q),vis[q]=1;
}
}
}
return true;
}
int main(){
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) add(0,i,0);
for(int i=1;i<=m;i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
add(a,b,-c);
}
if(!spfa()) printf("NO\n");
else for(int i=1;i<=n;i++) printf("%d ",dis[i]);
return 0;
}