標籤:差分約束系統
總的開說差分約束問題就是給出一系列不等式然後求問某一式子的最大值或者最小值。
差分約束問題詳解:
比如有這樣一組不等式:
X1 - X2 <= 0
X1 - X5 <= -1
X2 - X5 <= 1
X3 - X1 <= 5 不等式組(1)
X4 - X1 <= 4
X4 - X3 <= -1
X5 - X3 <= -3
X5 - X4 <= -3
全都是兩個未知數的差小於等於某個常數(大於等於也可以,因為左右乘以-1就可以化成小於等於)。這樣的不等式組就稱作差分約束系統。
這個不等式組要麼無解,要麼就有無數組解。因為如果有一組解{X1, X2, ..., Xn}的話,那麼對於任何一個常數k,{X1 + k, X2 + k, ..., Xn + k}肯定也是一組解,因為任何兩個數同時加一個數之後,它們的差是不變的,那麼這個差分約束系統中的所有不等式都不會被破壞。
差分約束系統的解法利用到了單源最短路徑問題中的三角形不等式。即對於任何一條邊u -> v,都有:
d(v) <= d(u) + w(u, v)
其中d(u)和d(v)是從源點分別到點u和點v的最短路徑的權值,w(u, v)是邊u -> v的權值。
顯然以上不等式就是d(v) - d(u) <= w(u, v)。這個形式正好和差分約束系統中的不等式形式相同。於是我們就可以把一個差分約束系統轉化成一張圖,每個未知數Xi對應圖中的一個頂點Vi,把所有不等式都化成圖中的一條邊。對於不等式Xi - Xj <= c,把它化成三角形不等式:Xi <= Xj + c,就可以化成邊Vj -> Vi,權值為c。
在建立圖的時候應該與目標函數的符號相同,即目標函數為>=,不等式的符號也應該變為>=,再根據不等式建立圖;
相反,如果是<=,不等式也應該全部變為<=,再建立圖;
(一致性)符號是>=或者<=,並非<和>
上文就講了如何根據不等式建立圖,哪到底和問題的不等式的最大值或者最小值有什麼關聯呢?
如題目要求d(v) - d(u)的最小值,可以轉換成d(v) - d(u) >=M,而根據上文得知M為u->v的權值。
要想上式子成立,則應有min(d(v) - d(u))>=max(M),即轉成了求u->v最長的路徑即式子的最小值
相同,如果求d(v) - d(u)的最大值,這應該求u->v的最小路徑
對於差分不等式,a - b <= c ,求的是最短路,得到的是最大值;
對於差分不等式 a - b >= c ,求的是最長路,得到的是最小值。
關於補不補充源點:
當d(v) 和d(u)都已在圖中存在,則不需要補充源點。
當圖沒有目標源點的時候或者需要串連整個圖的時候,則需要補充源點。並補充關係。
如果圖中存在負權值迴路,則求出來的最短路徑沒有意義。也就是說不等式無解。所以要從是否有負權值迴路進而判斷有無解。
zoj求最小值:(差分不等式a-b>=c,求最長路,得到最小值)
#include <map>#include <set>#include <queue>#include <stack>#include <math.h>#include <vector>#include <cstdio>#include <string>#include<string.h>#include <iostream>#include <algorithm>using namespace std;typedef long long ll;int dist[1010];int n,m,a,b,c;struct edge{int to,cost;}cur;vector<edge>vec[1010];inline void read(int &m)//int{int x=0,f=1;char ch=getchar();//intwhile(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}bool spfa() //(差分不等式a-b>=c,求最長路,得到最小值){int cnt[1010];bool vis[1010];for(int i=1;i<=n;i++)dist[i]=-1111111111;memset(cnt,0,sizeof(cnt));memset(vis,0,sizeof(vis));vis[0]=1;dist[0]=0;queue<int>que;que.push(0);while(!que.empty()){int v=que.front();que.pop();if(++cnt[v]>=n)return 0;vis[v]=0;for(int i=0;i<vec[v].size();i++){edge temp=vec[v][i];if(dist[temp.to]<dist[v]+temp.cost){dist[temp.to]=dist[v]+temp.cost;if(!vis[temp.to]){vis[temp.to]=1;que.push(temp.to);}}}}return 1;}int main(){while(~scanf("%d %d",&n,&m)){for(int i=0;i<=n;i++)vec[i].clear();for(int i=1;i<=n;i++){read(a);cur.to=i;cur.cost=0;vec[i-1].push_back(cur); //Ai>=0 S(i-1)-Si<=0 cur.to=i-1;cur.cost=-a;vec[i].push_back(cur); //Ai<=a Si-S(i-1)<=0cur.to=i;cur.cost=0;vec[0].push_back(cur); //Si-S0>=0 //0點為新添上去的源點,與其他節點建立關係}for(int i=1;i<=m;i++){read(a),read(b),read(c);cur.to=b;cur.cost=c;vec[a-1].push_back(cur); //Sb-Sa-1>=c}if(spfa())printf("%d\n",dist[n]);else printf("Bad Estimations\n");}return 0;}