差分约束系统
一、算法简介
差分约束系统是一种特殊的N元一次不等式组,它包含N个变量,X1~Xn以及m个约束条件,每一个约束条件都是由两个变量作差构成的,形如Xi-Xj≤Ck,其中Ck是常数,1≤i,j≤N,1≤k≤M,解决的问题是求一组解,X1=a1,X2=a2~~~Xn=an,使得所有的约束条件都得到满足。
差分约束系统的每一个约束条件Xi-Xj≤Ck可以变形为最短路中的三角不等式,Xi≤Xj+Ck,因此,可以把每一个变量Xi看作有向图中的一个结点i,对于每一个约束条件Xi-Xj≤Ck,为从结点j想结点i连一条长度为Ck的有向边
二、例题
此题是差分约束的裸题,但我之前也不知道差分约束系统是什么。
定义:差分约束系统是一种特殊的N元一次不等式,包含N个变量及M个约束条件,每个约束条件由两个变量作差构成,形如Xi-Xj>=Ck,Ck是常数,1<=i,j<=N,1<=k<=M。如
X1-X0>=2
X2-X1>=3
X3-X2>=2
X3-X0>=6
通过消元可得到X3-X0的最小值为7。假若我们从0->1建一条权值为2的边,从1->2建一条权值为3的边,以此类推。从0到3跑一遍SPFA求最长路,发现也为7。
这不是个巧合,因为求最长路时,SPFA的松弛操作后,dis[v]>=dis[u]+g[i].w,与不等式Xi>=Xj+Ck性质一样,所以SPFA可以求出最小值,又因为不等式同大取大,所以要求出最长路。所以SPFA死了吗?
最后,一定要注意输出,每个答案空一行,最后一个答案最后要输出换行。
本题就是从0-50000选出最少的数,使每个区间至少有c个数被选,这是求最小值,所以我们待会要将所有的不等式转换成">="的形式 我们用数组d[i]表示0-i之间至少要选多少个数,所以对于每一个约束条件,可以看成d[b_i]-d[a_i-1]>=c_id[bi]−d[ai−1]>=ci
d[bi]>=ci+d[ai−1]
因此我们在a[i-1]和b[i]之间建一条长度为c[i]的边
上述建边过程只是题干中给出的条件,我们还要找出题目中的隐藏条件(隐藏条件视题目而为,有则加)。
因为d[i]表示的是0-i之间要选的最小个数,所以d[i]一定要大于d[i-1];又因为每个数只能选一次,所以d[i]-d[i-1]要么为0,要么为1;
由上述两个条件,我们对应出两个不等式d_i-d_{i-1}>=0di−di−1>=0d_{i-1}-d_{i}>=-1di−1−di>=−1
所以我们在i-1和i之间连长度为0的有向边,在i和i-1之间连长度为-1的有向边
因为i的范围是0-50000所以i-1会变成负数,我们都知道,若数组的下标为负,程序会运行错误,所以我们不妨给所有的变量加1,这样就不会出现负下标了,所以上述加边过程可简述为
1---->在a[i]和b[i]+1之间连一条长度为c[i]的有向边
2---->在i和i+1之间连一条长度为0的有向边
3---->在i+1和i之间连一条长度为-1的有向边
接着,设d[0]为1,以0为起点求单元最长路
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define inf 0x3f3f3f3f using namespace std; int t,n,head[500005],tot,ma,dis[500005]; bool vis[500005]; struct node{ int u,v,next,w; }g[5000005]; inline int rea(){//快读 int s=0,g=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') g=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return s*g; } inline void add(int u,int v,int w){//建图 g[++tot].u=u; g[tot].v=v; g[tot].w=w; g[tot].next=head[u]; head[u]=tot; } inline void spfa(){//SPFA求最长路,即可得出最小值 queue<int>q; q.push(0); vis[0]=true; dis[0]=0; while(!q.empty()){ int u=q.front(); q.pop(); vis[u]=false; for(int i=head[u];i;i=g[i].next){ int v=g[i].v; if(dis[v]<dis[u]+g[i].w){ dis[v]=dis[u]+g[i].w; if(!vis[v]){ vis[v]=true; q.push(v); } } } } } int main(){ t=rea(); while(t--){ memset(head,0,sizeof(head)); memset(dis,-0x3f,sizeof(dis)); tot=0; n=rea(); for(int i=1;i<=n;i=-~i){ int a=rea(),b=rea(),c=rea(); add(a,b+1,c);//因为前缀和中,Sb-S(a-1)>=c,然而a-1要大于等于0,a有可能是0,所以我们加一 ma=max(ma,b+1); } for(int i=1;i<=ma;i=-~i){ add(i-1,i,0); add(i,i-1,-1);//Si-S(i-1)>=0,S(i-1)-Si>=-1 } spfa(); printf("%d\n",dis[ma]); if(t) printf("\n");//注意输出!!! } return 0; }
三、相关文章链接
https://blog.csdn.net/dragon60066/article/details/80245797