图论 —— 差分约束系统
【概述】
如果一個(gè)系統(tǒng)由 n 個(gè)變量 m 個(gè)約束條件組成,形成 m 個(gè)形如??的不等式,其中?,k 是常數(shù),則稱這 m 個(gè)不等式為差分約束系統(tǒng)(system of difference constraints),亦即,差分約束系統(tǒng)是求解關(guān)于一組變量的特殊不等式組的方法。
如下圖,就是一個(gè)差分約束系統(tǒng)
【求解方法】
差分約束系統(tǒng)的求解可以轉(zhuǎn)化為圖論的最短路來(lái)解決。
對(duì)于三角不等式,有:
- B - A <= c ①
- C - B <= a ②
- C - A <= b ③
若要求 C-A 的最大值,由 ①+②
易知:
- C-A<=c+a
- C-A<=b
由不等式基本原理可得:max(C-A)= min(b,a+c),即對(duì)應(yīng)下圖 C 到 A 的最短路。?
因此對(duì)于最短路徑:d(v) <= d(u) + w(u, v) ,移項(xiàng)得:d(v) - d(u) <= w(u, v),其形式與 x-y<=b 相同。
因此,對(duì)三角不等式加以推廣,當(dāng)有 n 個(gè)變量 m 個(gè)不等式,要求 Xn-X1 的最大值,就是求建圖后的最短路,同樣地,若要求取差分約束系統(tǒng)中 Xn-X1 的最小值,就是求建圖后的最長(zhǎng)路。
如果建圖后對(duì)最短路的求取存在負(fù)環(huán),則說(shuō)明該差分約束系統(tǒng)無(wú)解。
【建圖與具體實(shí)現(xiàn)】
求解差分約束系統(tǒng)的關(guān)鍵在于建圖,建好圖后使用 SPFA 算法直接判斷有無(wú)負(fù)環(huán)即可判斷該差分約束系統(tǒng)有無(wú)解。
具體方法:
1.新建一個(gè)圖,n 個(gè)變量看作 n 個(gè)點(diǎn),m 個(gè)約束條件作為 m 條邊,每個(gè)頂點(diǎn) Vi 分別對(duì)于一個(gè)未知量,每個(gè)有向邊對(duì)應(yīng)兩個(gè)未知量的不等式。
? 1)對(duì)于?<= 的不等式,形如:dis[j]-dis[i]<=w,可化為 dis[j]<=dis[i]+w,建立從 i 到 j 權(quán)值為 w 的邊
? 2)對(duì)于 >= 的不等式,形如:dis[j]-dis[i]>=w,可化為 dis[i]<=dis[j]-w,建立從 j 到 i 權(quán)值為 -w 的邊
2.根據(jù)約束條件,進(jìn)行初始化
? 1)若求差最大,則:dis[1]=0 且 dis[i]=INF
? 2)若求差最小,則:dis[1]=0 且 dis[i]=-INF
3.根據(jù)實(shí)際情況判斷是否需要添加超級(jí)源點(diǎn) Vs(0 號(hào)點(diǎn))
? 1)若建成的圖能保證連通,則直接求最短路即可,若建成的圖不是連通圖,為保證圖的連通性,需要加一個(gè)源點(diǎn) Vs,從 Vs?到任意點(diǎn) Vi 的邊權(quán)為 0,即:W(Vs,Vi)=0,然后從該點(diǎn)出發(fā)進(jìn)行計(jì)算,那么最終求出源點(diǎn) Vs 到其他所有點(diǎn)的最短距離就是差分約束系統(tǒng)的一個(gè)可行解,且可行解之間的差距最小。
? ? ? ?原因:添加從虛點(diǎn) Vs 到各個(gè)頂點(diǎn) Vi 的權(quán)為 0 的邊,是為了保證構(gòu)造出的圖是連通的,且由于虛點(diǎn)本身并不引入負(fù)環(huán),因此設(shè)置虛點(diǎn) Vs 后最短路仍然存在且每個(gè)約束仍滿足。
? 2)若不添加超級(jí)源點(diǎn),只是將初始距離設(shè)為 INF,且令其中一個(gè)點(diǎn)的初始距離為 0,然后就該點(diǎn)到其他所有點(diǎn)的最短距離,那么最短距離的集合就是一個(gè)可行解,且該可行解兩兩之間差距最大。
適用情況:保證問(wèn)題一定存在解,即:不存在負(fù)環(huán)(否則從 1 號(hào)點(diǎn)到其他點(diǎn)沒(méi)有路,但其他點(diǎn)的強(qiáng)連通分量中有負(fù)環(huán))
4.根據(jù)結(jié)果進(jìn)行解的判斷
1)若源點(diǎn) Vs 到某點(diǎn) Xi 不存在最短路徑,即:dis[Xi]=INF,則表示該點(diǎn) Xi 表示的變量可取任意解,均能滿足差分約束的要求
2)若存在源點(diǎn) Vs 到某點(diǎn) Xi 的最短路徑,則得到該點(diǎn) Xi 表示的變量的最大值
3)若求最短路的過(guò)程中出現(xiàn)負(fù)環(huán),則該差分約束系統(tǒng)無(wú)解。
注:若所有的未知量都是正數(shù),則不會(huì)存在負(fù)環(huán),使用 Dijkstra 求解單源最短路即可;若存在負(fù)數(shù),需使用 SPFA 來(lái)求解,判斷有無(wú)負(fù)環(huán)
【模版】
1.Dijkstra 求解
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #define N 1000001 struct Edge{int from;int to;int w;Edge(){}Edge(int from,int to,int w):from(from),to(to),w(w){} }edge[N]; int head[N],next[N],num; int dis[N]; bool vis[N]; void add(int from,int to,int w){edge[num]=Edge(from,to,w);next[num]=head[from];head[from]=num++; } struct HeapNode{int dis;int x;HeapNode(int dis,int x):dis(dis),x(x){}bool operator < (const HeapNode &rhs) const{return dis>rhs.dis;} }; int dijkstra(int n){for(int i=0;i<n;i++)dis[i]=INF;dis[0]=0;priority_queue<HeapNode> Q;Q.push(HeapNode(dis[0],0));while(!Q.empty()){HeapNode x=Q.top();Q.pop();int u=x.x;if(vis[u])continue;vis[u]=true;for(int i=head[u];i!=-1;i=next[i]){Edge &e=edge[i];if(dis[e.to]>dis[u]+e.w){dis[e.to]=dis[u]+e.w;Q.push(HeapNode(dis[e.to],e.to));}}}return dis[n-1]; } int main(){int n,m;scanf("%d%d",&n,&m);num=0;memset(head,-1,sizeof(head));while(m--){int x,y,w;scanf("%d%d%d",&x,&y,&w);x--,y--;add(x,y,w);}int res=dijkstra(n);if(res==INF)printf("arbitrary");else//源點(diǎn)到終點(diǎn)的最大值printf("%d\n",res);return 0; }2.SPFA 求解
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define INF 0x3f3f3f3f #define N 10001 struct Edge{int from;int to;int w;Edge(){}Edge(int from,int to,int w):from(from),to(to),w(w){} }edge[N]; int head[N],next[N],num; int dis[N]; int outque[N];//記錄出隊(duì)次數(shù) bool vis[N]; void init(){num=0;memset(head,-1,sizeof(head)); } void add(int from,int to,int w){edge[num]=Edge(from,to,w);next[num]=head[from];head[from]=num++; } void SPFA(int s,int n){int res=0;memset(vis,false,sizeof(vis));memset(outque,0,sizeof(outque));for(int i=1;i<n;i++)dis[i]=INF;dis[s]=0;vis[s]=true;queue<int> Q;Q.push(s);while(!Q.empty()){int x=Q.front();Q.pop();vis[x]=false;outque[x]++;if(outque[x]>n-1){//如果出隊(duì)次數(shù)大于n,說(shuō)明存在負(fù)環(huán)res=-1;break;}for(int i=head[x];i!=-1;i=next[i]){Edge &e=edge[i];if(dis[e.to]>dis[x]+e.w){dis[e.to]=dis[x]+e.w;if(!vis[e.to]){vis[e.to]=true;Q.push(e.to);}}}}if(res==-1)//出現(xiàn)負(fù)環(huán),不存在可行解printf("-1\n");else if(dis[n]==INF)//可取任意值printf("arbitrary\n");else//源點(diǎn)s到終點(diǎn)的最大值printf("%d\n",dis[n]); } int main(){int n,m;while(scanf("%d%d",&n,&m)!=EOF){init();for(int i=0;i<m;i++){int x,y,w;scanf("%d%d%d",&x,&y,&w);add(x,y,w);//建邊x-y<=w}//虛擬節(jié)點(diǎn)0號(hào)到各點(diǎn)的邊//for(int i=1;i<=n;i++)//add(0,i,0);SPFA(1,n);//求源點(diǎn)s到終點(diǎn)的最大值,若添加虛擬節(jié)點(diǎn)0號(hào)后,需改為SPFA(0,n+1)}return 0; }【例題】
總結(jié)
以上是生活随笔為你收集整理的图论 —— 差分约束系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 吃糖果(HDU-1205)
- 下一篇: 理论基础 —— 索引 —— 分块索引