差分约束系统
差分约束系统是求解关于一组变量的特殊不等式组的方法,系统由(n)个变量和(m)个不等式组成,其中每个不等式的形式为
或者
分别对应两数差的最大值(最短路)和最小值(最长路)
差分约束系统的求解方法
差分约束系统可以转化为图论的最短路或者最长路解决
由于在最短路算法的松弛过程中,(dis[v]leq dis[u]+w(u,v)),也就是(dis[v]-dis[u]leq w(u,v)),与差分约束系统中不等式的形式相似,所以对于每个不等式,从(x_j)向(x_i)连接一条边。计算(x_n-x_1)的最大值,等价于计算从(x_1)到(x_n)的最短路
类似地,计算(x_n-x_1)的最小值,等价于计算从(x_1)到(x_n)的最长路
所以首先可以根据求解的是最大值还是最小值,改变不等号的方向,使得不等式组中所有不等号的方向一致。最小值对应的不等号为(leq),最大值对应的不等号为(geq)
建图之后不一定存在最短路或者最长路,也就是差分约束系统不一定有解,判断是否有解等价于判断图中是否存在负环((Bellman-Ford)或者(SPFA))
相关题目
hdu3440 House Man 求解最大值
给出一列数作为房屋高度,其中所有的高度值都不相同,按照高度从低到高的顺序逐个在房顶上跳,每个房屋被跳一次,每次跳的两个房屋之间的距离不超过(d)。同一个位置最多只有一个房屋,在不改变原来房屋排列顺序的情况下安排所有房屋之间的距离,计算最低的房屋和最高的房屋之间距离的最大值。
设高度为(h)的房屋在给定的排列中的顺序为(a_h),按照高度排序之后第(i)个房屋的高度为(b_i)。由于高度相邻的两个房屋之间的距离不超过(d),所以有(max(a_{b_i},a_{b_{i-1}})-min(a_{b_i},a_{b_{i-1}})leq d)
由于同一个位置最多只有一个房屋,所以有(a_i-a_{i-1}geq 1),也就是(a_{i-1}-a_ileq -1)
所以共有两种类型的不等式:
由于求解的是最大值,所以建图通过(spfa)算法计算从(min(a_{b_1},a_{b_n}))到(max(a_{b_1},a_{b_n}))的最短路
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=1010;
int T,n,d,head[maxn],to[4*maxn],nxt[4*maxn],weight[4*maxn],cnt;
int inq[maxn],k[maxn];
LL a[maxn],b[maxn],dis[maxn];
map<LL,int> mp;
void add(int u,int v,int w){
to[cnt]=v;
weight[cnt]=w;
nxt[cnt]=head[u];
head[u]=cnt++;
}
bool spfa(int s){
for(int i=1;i<=n;i++){
dis[i]=(i==s)?0:1e18;
inq[i]=(i==s)?1:0;
k[i]=(i==s)?1:0;
}
queue<int> q;
q.push(s);
while(!q.empty()){
int p=q.front();
q.pop();
inq[p]=0;
for(int i=head[p];~i;i=nxt[i]){
int v=to[i],w=weight[i];
if(dis[v]>dis[p]+w){
dis[v]=dis[p]+w;
k[v]++;
if(k[v]>=n) return true;
if(!inq[v]){
inq[v]=1;
q.push(v);
}
}
}
}
return false;
}
int main(){
scanf("%d",&T);
for(int cas=1;cas<=T;cas++){
scanf("%d %d",&n,&d);
mp.clear();
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
mp[a[i]]=i;
b[i]=a[i];
}
sort(b+1,b+1+n);
memset(head,-1,sizeof(head));
cnt=0;
for(int i=2;i<=n;i++){
int u=mp[b[i-1]];
int v=mp[b[i]];
add(min(u,v),max(u,v),d);
}
for(int i=2;i<=n;i++){
add(i,i-1,-1);
}
printf("Case %d: ",cas);
int mini=mp[b[1]];
int maxi=mp[b[n]];
if(!spfa(min(mini,maxi))){
printf("%lld
",dis[max(mini,maxi)]);
}
else printf("-1
");
}
return 0;
}
hdu1384 Intervals 求解最小值
给出(n)个区间,每个区间为([a_i,b_i]),选取一些数,使得区间(i)中的数的个数不少于(c_i),计算选取的数的个数的最小值
设(S_i)表示([1,i])中选取的数的个数的最小值,则对于每一个区间([a_i,b_i]),有不等式(S_{b_i}-S_{a_i-1}geq c_i)
同时存在不等式(0leq S_i-S_{i-1}leq 1),拆分成两个不等式(S_igeq S_{i-1}+0)和(S_{i-1}geq S_i-1)
所以共有三种不等式:
由于求解的是最小值,所以建图通过(spfa)算法计算从(S_{mini})到(S_{maxi})的最长路
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<string>
#include<sstream>
#include<cmath>
#include<ctime>
#include<algorithm>
#define LL long long
#define PII pair<int,int>
#define PLL pair<LL,LL>
#define pi acos(-1.0)
#define eps 1e-6
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=50010,inf=0x3f3f3f3f;
int n,head[maxn],nxt[4*maxn],to[4*maxn],weight[4*maxn],cnt,inq[maxn];
LL dis[maxn];
queue<int> q;
void add(int u,int v,int w){
to[cnt]=v;
weight[cnt]=w;
nxt[cnt]=head[u];
head[u]=cnt++;
}
void spfa(int mini,int maxi){
for(int i=mini+1;i<=maxi;i++){
dis[i]=-1e18;
inq[i]=0;
}
dis[mini]=0;
inq[mini]=1;
q.push(mini);
while(!q.empty()){
int u=q.front();
q.pop();
inq[u]=0;
for(int i=head[u];~i;i=nxt[i]){
int v=to[i],w=weight[i];
if(dis[v]<dis[u]+w){
dis[v]=dis[u]+w;
if(!inq[v]){
q.push(v);
inq[v]=1;
}
}
}
}
}
int main(){
while(~scanf("%d",&n)){
memset(head,-1,sizeof(head));
cnt=0;
int mini=inf,maxi=-1;
for(int i=1;i<=n;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
add(u-1,v,w);
mini=min(mini,u-1);
maxi=max(maxi,v);
}
for(int i=mini+1;i<=maxi;i++){
add(i-1,i,0);
add(i,i-1,-1);
}
spfa(mini,maxi);
printf("%lld
",dis[maxi]);
}
return 0;
}