昨天看了下差分約數系統的含義,其實就是如果有n個變量在m個形如aj-ai>=bk條件下,求解的此不等式的方法。
而這種不等式的解法其實就是轉化為圖論的最小路的算法求解的。我們將上面的不等式邊形后得到aj>=ai+bk正好就可以看做是從ai到aj權值是bk的一條路徑最短的邊。這樣一來,只要依照題目的條件寫出一系列這樣的不等式,也就是相當於按照題意增加了一些合法的邊,也就完全轉化為了最短路的算法。
再看這道題,題目說[ai, bi]區間內和點集Z至少有ci個共同元素,那也就是說如果我用Si表示區間[0,i]區間內至少有多少個元素的話,那么Sbi - Sai >= ci,這樣我們就構造出來了一系列邊,權值為ci,但是這遠遠不夠,因為有很多點依然沒有相連接起來(也就是從起點可能根本就還沒有到終點的路線),此時,我們再看看Si的定義,也不難寫出0<=Si - Si-1<=1的限制條件,雖然看上去是沒有什么意義的條件,但是如果你也把它構造出一系列的邊的話,這樣從起點到終點的最短路也就順理成章的出現了。
我們將上面的限制條件寫為同意的形式:
Sbi - Sai >= ci
Si - Si-1 >= 0
Si-1 - Si >= -1
這樣一來就構造出了三種權值的邊,而最短路自然也就沒問題了。
但要注意的是,由於查分約束系統里常常會有負權邊,所以為了避免負權回路,往往用Bellman-Ford或是SPFA求解(存在負權回路則最短路不存在)。
1 #include <map> 2 #include <set> 3 #include <stack> 4 #include <queue> 5 #include <cmath> 6 #include <ctime> 7 #include <vector> 8 #include <cstdio> 9 #include <cctype> 10 #include <cstring> 11 #include <cstdlib> 12 #include <iostream> 13 #include <algorithm> 14 using namespace std; 15 #define eps 1e-15 16 #define MAXN 50005 17 #define INF 1000000007 18 #define MAX(a,b) (a > b ? a : b) 19 #define MIN(a,b) (a < b ? a : b) 20 #define mem(a) memset(a,0,sizeof(a)) 21 22 struct EDGE//使用鄰接表存邊 23 { 24 int v; 25 int w; 26 int next; 27 }edge[3*MAXN]; 28 int head[MAXN], d[MAXN], vis[MAXN],N, n, Max,Min; 29 30 void AddEdge(int u,int v,int w)//添加新邊 31 { 32 edge[N].v = v; 33 edge[N].w = w; 34 edge[N].next = head[u]; 35 head[u] = N++; 36 } 37 38 void SPFA()//此題數據較大,用Bellman-ford恐怕過不了 39 { 40 for(int i=Min;i<=Max;i++) d[i] = -INF; 41 d[Min] = 0; 42 queue<int>q; 43 q.push(Min); 44 while(!q.empty()) 45 { 46 int x = q.front(); q.pop(); 47 vis[x] = 0; 48 for(int e=head[x];e != -1;e=edge[e].next)if(d[edge[e].v] < d[x] + edge[e].w) 49 { 50 d[edge[e].v] = d[x] +edge[e].w; 51 if(!vis[edge[e].v]) 52 { 53 q.push(edge[e].v); 54 vis[edge[e].v] = 1; 55 } 56 } 57 }//由於題目說一定有解,我就略去了判斷是否存在負權回路 58 } 59 60 int main() 61 { 62 while(~scanf("%d", &n)) 63 { 64 int u,v,w; N = 0; 65 memset(head,-1,sizeof(head)); 66 mem(vis); mem(edge); 67 Min = INF; Max = -INF; 68 for(int i=0;i<n;i++) 69 { 70 scanf("%d%d%d", &u,&v,&w);//題目條件的邊 71 AddEdge(u,v+1,w); 72 Min = MIN(Min, u);//在這里記錄最小值和最大值,所求的就是以Min為源點 73 Max = MAX(Max, v+1);//以Max為終點的最短路 74 } 75 for(int i = Min;i < Max; i++)//添加新邊 76 { 77 AddEdge(i,i+1,0); 78 AddEdge(i+1,i,-1); 79 } 80 SPFA(); 81 printf("%d\n", d[Max]); 82 } 83 return 0; 84 }
