百科是这样说的:如果一个系统由n个变量和m个约束条件组成,其中每个约束条件形如xj-xi<=bk(i,j∈[1,n],k∈[1,m]),则称其为差分约束系统。亦即,差分约束系统是求解关于一组变量的特殊不等式组的方法。
学习差分约束系统之前,必须掌握单源最短路径的算法(Bellman-ford、SPFA)。习惯上用SPFA解差分约束的题目。
解题思想有两种:
假设j > i,
1、由xj - xi >=w得到xj >= xi + w,建立一条从xi->xj的边权为w的边。然后用最长路算法。
2、由xj - xi >=w得到xi <= xj - w,建立一条从xj->xi的边权为-w的边。然后用最短路算法。
在具体建图的时候,为了避免重复,j+1或者i-1。
实际上,最长路和最短路算法的区别很小(SPFA模版)。
最长路算法中dist[]初始化为负无穷,松弛的时候是if(dist[v] < dist[u] + Map[u][v]) 。
最短路算法中dist[]初始化为正无穷,松弛的时候是if(dist[v] > dist[u] + Map[u][v]) 。
以具体的题目作为例子吧。
hdu1384
题目大意是:
给出一些区间[ai,bi]和每个区间最少需要ci个点,然后问总共最少需要几个点满足所有区间的要求。
由于1<=ai<=bi,所以ai-1或者bi+1都是没关系的,都是可以的。(下标从0开始和1开始都行,却不能是负数)
设s[i]是1~i 中需要的点的个数。则
s[j+1]-s[i]>=w。这只是题目给出的数据所能建的图。
但是有很多点是没有连通的。
有隐含条件 0=<s[i+1] - s[i] <=1 。
这样就可以做题了。
如果选择建立xj >= xi + w的关系来建图,就需要用最长路经来解题。dist[]初始化为负无穷。
具体的看代码:
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #include<algorithm> using namespace std; const int N = 50010, M=150010; const int INF = 0x3f3f3f3f; struct node { int to, w, next; }; node edge[M]; int head[N], dist[N], outq[N]; bool vis[N]; int tot; bool SPFA(int s, int n ) { int i,k; for(i=0;i<=n;i++) dist[i]=-INF; memset(vis,0,sizeof(vis)); memset(outq,0,sizeof(outq)); queue<int > q; while(!q.empty()) q.pop(); vis[s]=1; dist[s]=0; q.push(s); while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; outq[u]++; if(outq[u]>n) return 0 ; k=head[u]; while(k>=0) { if(dist[edge[k].to]-edge[k].w<dist[u]) { dist[edge[k].to]=dist[u]+edge[k].w; if(!vis[edge[k].to]) { vis[edge[k].to]=1; q.push(edge[k].to); } } k=edge[k].next; } } return 1; } void addedge(int i, int j ,int w) { edge[tot].to=j; edge[tot].w=w; edge[tot].next=head[i]; head[i]=tot++; } void init() { memset(head,-1,sizeof(head)); tot=0; } int main() { //freopen("test.txt","r",stdin); int n,m,i,j,k,w,a,b; while(scanf("%d",&m)!=EOF) { init(); a=INF,b=-INF; for(k=0;k<m;k++) { scanf("%d %d %d",&i,&j,&w); if(a>i) a=i; if(b<j+1) b=j+1; addedge(i,j+1,w); } for(i=a;i<=b;i++) { addedge(i,i+1,0); addedge(i+1,i,-1); } if(SPFA(a,b)) printf("%d\n",dist[b]); } return 0; }