差分约束简析
差分约束
定义
有 \(n\) 个变量 \(x_1,x_2,...,x_n\) 和 \(m\) 个形如 \(x_i-x_j\le c_k\) 的不等式约束,其中 \(c_k\) 是常量,求出可能的解,显然只要有解,在不限定范围的情况下,就有无数组解
求解过程
我们先讨论求变量最大值的情况,由于是最大值,不等式的关系就应该是我们求的变量小于某个值,将题目信息转变为不等式 \(x_i-x_j\le c_k\) 再变形为 \(x_i\le c_k+x_j\)
不难发现,我们可以利用最短路中松弛的思想建出一条边,那么对这条边进行松弛操操作就是要求满足这个条件,那么我们只需要对每一个不等关系都建一条边跑一边看一下是否有负环就行了,只要有负环就说明无解
现在还有一个问题,每对不等关系中都有两个变量,这样无法求出具体的值,只有相对关系,所以我们开一个超级源点,令它为零并连向所有点,题目中一般规定了 \(x\) 的范围,例如 $x\ge k $ 那么我们便有 \(0\) 连向所有 \(x\) 权值为 k 的点,限制了所有点的最小值为 \(k\),让 \(x\) 成为定值
最短路和最长路的使用
还是先研究最大值,对于单个变量,我们有限制条件 \(x_1-x_2\le c_1\) 同时我们可以有 \(x_2-x_3\le c_2\)
所以有 \(x_1\le c_1+x_2\le c_1+c_2+x_3\) 以此类推 \(x_1\) 需要取得这若干对关系的最小值,也就是到达 \(x_1\) 的最短路,所以如果我们要求得最小值,反而要使用最短路
最大值则相反 ,我们需要建立 \(x_1-x_2\ge c_1\)的关系式利用最长路求解
P3275 SCOI2011 糖果 - 洛谷
这道题虽然使用SPFA会超时,但是是求最小值的典例,它告诫我们差分约束要合理辨析最短路和最长路的使用,附上代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int N,K;
const int M=3e6+10;
const int L=1e6+10;
int cnt;
struct Edge{
int to;
int w;
int ne;
}e[M];
int h[M];
int idx[M];
bool vis[M];
long long dist[M];
void add(int x,int y,int w){
e[cnt].to=y;
e[cnt].ne=h[x];
e[cnt].w=w;
h[x]=cnt++;
}
bool spfa(){
queue<int> q;
memset(dist,0x3f,sizeof dist);
vis[0]=1;
idx[0]++;
dist[0]=0;
q.push(0);
while(q.size()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=h[u];~i;i=e[i].ne){
int v=e[i].to;
if(dist[v]>dist[u]+e[i].w){
dist[v]=dist[u]+e[i].w;
if(!vis[v]){
vis[v]=1;
q.push(v);
idx[v]++;
if(idx[v]>=N+1) return false;
}
}
}
}
return true;
}
signed main(){
cin>>N>>K;
memset(h,-1,sizeof(h));
for(int i=1;i<=K;i++){
int k,a,b;
cin>>k>>a>>b;
if(k==1){
add(b,a,0);
add(a,b,0);
}
else if(k==2){
add(b,a,-1);
}
else if(k==3){
add(a,b,0);
}
else if(k==4){
add(a,b,-1);
}
else if(k==5){
add(b,a,0);
}
}
for(int i=1;i<=N;i++){
add(0,i,1);
}
if(!spfa()) cout<<-1;
else{
long long res=0;
for(int i=1;i<=N;i++){
res+=dist[i];
}
cout<<res;
}
return 0;
}

浙公网安备 33010602011771号