差分约束
差分约束
(1) 求不等式组的可行解
步骤:
[1] 先将每个不等式 xi < = xj + ck 转换成一条从xj 走到 xi ,长度为ck的一条边 即表示为 离源点距离dist[i]<=dist[j]+ck
[2] 找一个超级源点,使得该源点一定可以遍历到所有边。
[3]从源点求一遍单源最短路
结果1:如果存在负环,则不等式组一定无解
结果2:如果没有负环,则dist[i]就是不等式组的一个可行解
(2) 如何求得最大值或最小值,最值是每个变量的最值。
结论:
①如果求的是最小值,则应该求最长路(在所有下界里,求最大值)//下界就是去为了求最小,最大值就是求能满足条件的下界中的最大值,所求即最小值。
注意:如果存在正环,则无解。
②如果求的是最大值,则应该求最短路。(上界的最小值)。
注意:如果存在负环,则无解。
如果求最小值,则应该是最长路,然后把所有的符号都变为>或>=
如果求最大值,则应该是最短路,然后把所有的符号都变为<或<=
例题
cWing 1169. 糖果
AC代码:
注意:边要加上超级源点的边,所以至少开2*m + n个边,否则会wa
#include <iostream>
#include <cstring>
#include <stack>
using namespace std;
/*
1 a==b a>=b+0 b>=a+0
2 a<b b>=a+1
3 a>=b a>=b
4 a>b a>=b
5 a<=b b>=a
并且建立一个超级源点
x=0
i>=x+1
建边时: i > = j + s 则 add(j,i,s)
后面的指向前边的,长度为s
*/
const int N = 1e5+5,M = 5e5+10;
typedef long long ll;
ll h[N],e[M],ne[M],idx;
ll w[M];
void add(ll a,ll b,ll c){
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
ll dist[N];
bool vis[N];
ll cnt[N];
stack<ll> q;
ll n,k;
bool spfa(){//在这里求最长路,并且要判断有无正环
memset(dist,-0x3f,sizeof dist);
dist[0]=0;
q.push(0);
vis[0]=true;
while(q.size()){
ll t=q.top();
q.pop();
vis[t]=false;
for(ll i=h[t];i!=-1;i=ne[i]){
ll j=e[i];
if(dist[j]<w[i]+dist[t]){
dist[j]=w[i]+dist[t];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n+1) return false;
if(!vis[j]){
q.push(j);
vis[j]=true;
}
}
}
}
return true;
}
int main(){
scanf("%lld%lld",&n,&k);
memset(h,-1,sizeof h);
ll x,a,b;
for(ll i=0;i<k;i++){
scanf("%lld%lld%lld",&x,&a,&b);
if(x==1) add(b,a,0),add(a,b,0);
else if(x==2) add(a,b,1);
else if(x==3) add(b,a,0);
else if(x==4) add(b,a,1);
else add(a,b,0);
}
for(ll i=1;i<=n;i++) add(0,i,1);
if(!spfa()){
printf("-1\n");
}else{
long long ans=0;
for(ll i=1;i<=n;i++) {
ans+=dist[i];
}
printf("%lld\n",ans);
}
}