1. 程式人生 > >差分約束系統【模板】

差分約束系統【模板】

差分約束系統:如果一個系統由n個變數和m個約束條件組成,其中每個約束條件形如 xj - xi<= bk ( i , j ∈ [1,n],k ∈ [1,m]),則稱其為差分約束系統。
例如如下的約束條件:
X1 - X2 <= 0 X1 - X5 <= -1
X2 - X5 <= 1 X3 - X1 <= 5
X4 - X1 <= 4 X4 - X3 <= -1
X5 - X3 <= -3 X5 - X4 <= -3
全都是兩個未知數的差小於等於某個常數(大於等於也可以,因為左右乘以-1就可以化成小於等於)。這樣的不等式組就稱作差分約束系統。
差分約束系統求解過程:
1.新建一個圖,N個變數看作N個頂點,M個約束條件作為M條邊。每個頂點Vi分別對於一個未知量,每個有向邊對應兩個未知量的不等式。
2.為了保證圖的連通性,在圖中新加一個節點Vs,圖中每個節點Vi都能從Vs可達,建立邊w(Vs,Vi) = 0。
3.對於每個差分約束Xj - Xi <= Bk(這裡是小於等於號),則建立邊w(Xi,Xj) = Bk。
4.初始化Dist[] = INF,Dist[Vs] = 0.
5.求解以Vs為源點的單源最短路徑,推薦用SPFA,因為一般可能存在負值。
如果圖中存在負權迴路,則該差分約束系統不存在可行解。
Vs到某點如果不存在最短路徑,即最短路為INF,則對於該點表示的變數可以取任意值,都能滿足差分約束的要求,如果存在最短路徑,則得到該變數的最大值。
上述過程最終得到的解為滿足差分約束系統各項的最大值。
注意點:
1. 如果要求最大值想辦法把每個不等式變為標準 x - y <= k 的形式,然後建立一條從 y 到 x 權值為 k 的邊,變得時候注意 x - y < k => x - y <= k-1。
2. 如果要求最小值的話,變為 x - y >= k 的標準形式,然後建立一條從 y到 x 權值為 k 的邊,求出最長路徑即可。
3. 如果權值為正,用Dijkstra,SPFA,BellmanFord都可以,如果為負不能用Dijkstra,並且需要判斷是否有負環,有的話就不存在。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
#define INF 0x7fffffff
using namespace std;
const int MAXN = 1100;
const int MAXM = 30030;

struct EdgeNode
{
    int to;
    int w;
    int next;
}Edges[MAXM];

int Head[MAXN],Dist[MAXN],vis[MAXN],outque[MAXN],id;

void
AddEdges(int u,int v,int w) { Edges[id].to = v; Edges[id].w = w; Edges[id].next = Head[u]; Head[u] = id++; } void SPFA(int s,int N) { int ans = 0; memset(vis,0,sizeof(vis)); memset(outque,0,sizeof(outque)); for(int i = 1; i <= N; ++i) Dist[i] = INF; Dist[s] = 0
; vis[s] = 1; queue<int> Q; Q.push(s); while( !Q.empty() ) { int u = Q.front(); Q.pop(); vis[u] = 0; outque[u]++; if(outque[u] > N+1) //如果出隊次數大於N,則說明出現負環 { ans = -1; break; } for(int i = Head[u]; i != -1; i = Edges[i].next) { int temp = Dist[u] + Edges[i].w; if(temp < Dist[Edges[i].to]) { Dist[Edges[i].to] = temp; if( !vis[Edges[i].to]) { vis[Edges[i].to] = 1; Q.push(Edges[i].to); } } } } if(ans == -1) //出現負權迴路,不存在可行解 printf("-1\n"); else if(Dist[N] == INF) //可取任意值,都滿足差分約束系統 printf("-2\n"); else printf("%d\n",Dist[N]); //求使得源點 s 到 終點 t 的最大的值 } int main() { int N,ML,MD,u,v,w; while(~scanf("%d%d%d", &N, &ML, &MD)) { memset(Head,-1,sizeof(Head)); id = 0; for(int i = 0; i < ML; ++i) { scanf("%d%d%d",&u,&v,&w); AddEdges(u,v,w);//建邊 u - v <= w } for(int i = 0; i < MD; ++i) { scanf("%d%d%d",&u,&v,&w); AddEdges(v,u,-w);//建邊 v - u <= w } //這裡不加也可以 // for(int i = 1; i < N; ++i) // AddEdges(i+1,i,0); SPFA(1,N); //求使得源點 s 到 終點 t 的最大的值 } return 0; }