图论3—差分约束

差分约束

运用范围:(求解多个二元不等式组)

例如:

X 1 − X 2 < = 0 X1-X2<=0 X1X2<=0
X 3 − X 4 < = 8 X3-X4<=8 X3X4<=8
X 2 − X 5 < = − 9 X2-X5<=-9 X2X5<=9
X 3 − X 1 < = 7 X3-X1<=7 X3X1<=7
X 5 − X 2 < = 0 X5-X2<=0 X5X2<=0
. . . . . . ...... ......

原理:

  1. 对于这类不等式组,其解集要么无解,要么有无数解。(所有数同时加上X,其差值不会改变)
  2. 我们可以用最短路的思想解决这类问题

在对一张图跑完最短路算法后,对任意一条由u指向v的边都存在不等式:
Dis[v]<=Dis[u]+Len[u][v]
将这个不等式移项有:
Dis[v]-Dis[u]<=Len[u][v]
这与不等式: X 1 − X 2 < = M X1-X2<=M X1X2<=M神似。
所以我们只需要把 X 1 X1 X1当作v点, X 2 X2 X2当作u点,再向两点连一条边长为m的边即可。
最后这个不等式组就会变成一张图。

如何判断是否有解?

当你构造的这张图存在负权回路就表示无解
(若存在负权回路则Dis数组会无限减少,最终不满足不等式)

如何求出一组具体的解

  1. 新建一个原点 X 0 X0 X0,把 X 0 X0 X0向所有点连一条权值为0的边
  2. X 0 X0 X0为起点跑一遍最短路。
  3. 对每一个节点的Dis[i],就是 X i Xi Xi的一个可行解
  4. 若把Dis[ X 0 X0 X0]设置为A时,所有其它节点的值都会小于A(不等式定义)

例题

AC代码:

#include<stdio.h>
#include<bits/stdc++.h>
using namespace std;
#define H 500005
#define LL long long
int N,M;
int Dis[H],ED[H],LA[H],NT[H],LEN[H],tot,Cnt[H];
queue<int>Q;
void LB(int u,int v,int d){
	ED[++tot]=v;NT[tot]=LA[u];LA[u]=tot;LEN[tot]=d;
}
bool flag=1,MK[H];
void SPFA(int S){
	for(int i=1;i<=N;i++)Dis[i]=1000000000;
	Q.push(S);
	MK[S]=1;Cnt[S]++; 
	while(!Q.empty()){
		int x=Q.front();
		Q.pop();MK[x]=0;
		for(int i=LA[x];i;i=NT[i]){
			int y=ED[i];
			if(Dis[y]>Dis[x]+LEN[i]){
				Dis[y]=Dis[x]+LEN[i];
				if(!MK[y]){
					MK[y]=1;Cnt[y]++;
					if(Cnt[y]>N){
						flag=0;return;
					}
					Q.push(y);
				}
			}
		}
	}
}
int main(){
	scanf("%d%d",&N,&M);
	for(int i=1;i<=M;i++){
		int u,v,d;
		scanf("%d%d%d",&v,&u,&d);
		LB(u,v,d);
	}
	for(int i=1;i<=N;i++)LB(0,i,0);
	SPFA(0);
	if(!flag)puts("NO");
	else{
		for(int i=1;i<=N;i++)printf("%d ",Dis[i]);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

go_bananas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值