差分约束系统
差分约束系统
差分约束系统是求解关于一组变量的特殊不等式组的方法,系统由\(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_i\leq -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\n",dis[max(mini,maxi)]);
}
else printf("-1\n");
}
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\)
同时存在不等式\(0\leq S_i-S_{i-1}\leq 1\),拆分成两个不等式\(S_i\geq 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\n",dis[maxi]);
}
return 0;
}