本篇作为最短路系列的扩展,讲如何用图论知识对差分约束系统进行建模并求解。
1. 线性规划问题(Linear Program)
假设给定了一个m×n矩阵A,列向量x跟b,线性规划问题可以描述为在约束
下求解向量
使得目标函数最大。其中未知量的数目为n,约束数目为m。
2.差分约束系统
所谓差分约束系统是线性规划的一种特殊情况,其中矩阵A的每一行分别有一个1跟-1其余元素为0。每个约束都形如
3.如何用最短路算法求解差分约束系统
首先构造约束图(Constraint Graph)
其中边上的权值为
。
下面的定理说明,可以用Bellman-Ford算法来求解差分约束系统。
Theorem 1 设差分约束系统对应的约束图为G=(V,E)。若该图无负权的环,则差分约束系统的一个可行解为。若该图有由源可待的带负权的环,则该系统无可行解。
Proof 由于带权有向图最短路若有解,则解
符合三角不等式
,即。
因此x是该系统的一个可行解。
若约束图存在由源可达的带负权的环,不失一般性,设该回路为
。
用反证法,若该图有解,则有满足
将上面式子相加,左边为0,右边为该回路的权值小于0,得到0<0,矛盾。
下面给出一个程序。该程序输入变量数目、约束数目以及具体的约束。若有解则输出解,否则报错。
1 #include <iostream>
2 #define MAXN 200
3 #define INF 0xfffffff
4
5 using namespace std;
6
7 int list[MAXN][MAXN][2]; // 图的链接表存储
8 int deg[MAXN]; // 每个顶点的出度
9 int delta[MAXN]; // 最终存储最短路的距离
10 int trace[MAXN]; // trace[i]表示s到i的最短路i的前驱
11 int n,e; // 顶点数
12
13 void initialize_single_source()
14 {
15 int i;
16 for(i=1;i<=n;i++) delta[i]=INF;
17 trace[i]=-1;
18 }
19
20 void relax(int a,int b)
21 {
22 if(delta[list[a][b][0]]>delta[a]+list[a][b][1])
23 delta[list[a][b][0]]=delta[a]+list[a][b][1];
24 trace[list[a][b][0]]=a;
25 }
26
27 bool checkNegtiveCycle()
28 {
29 int i,j,k;
30 for(i=0;i<=n;i++)
31 {
32 for(j=0;j<deg[i];j++)
33 {
34 if(delta[list[i][j][0]]>delta[i]+list[i][j][1]) return false;
35 }
36 }
37 return true;
38 }
39
40 bool bellman_ford()
41 {
42 int i,j,k;
43 for(i=1;i<n;i++)
44 {
45 for(j=0;j<=n;j++)
46 {
47 for(k=0;k<deg[j];k++)
48 {
49 // 这里j跟k的含义不同:j是顶点编号,k是第j个顶点的第k条边
50 relax(j,k);
51 }
52 }
53 }
54 return checkNegtiveCycle();
55 }
56
57
58
59 void printSolution()
60 {
61 int i;
62 for(i=1;i<=n;i++) printf("x_{%d}=%d\n",i,delta[i]);
63 return;
64 }
65
66 int main()
67 {
68 int i,j,k,u,v,b;
69 // 输入未知量的个数
70 printf("Enter the number of unknowns:");
71 scanf("%d",&n);
72 // 输入约束数
73 printf("Enter the number of constrains:");
74 scanf("%d",&e);
75 // 输入约束
76 printf("Enter constrains in the format of xi(-)xj(<=)bk\n");
77 for(i=1;i<=e;i++) deg[i]=0;
78 for(i=1;i<=e;i++)
79 {
80 scanf("%d%d%d",&v,&u,&b);
81 list[u][deg[u]][0]=v;
82 list[u][deg[u]][1]=b;
83 deg[u]++;
84 }
85 // 添加新的顶点
86 deg[0]=n;
87 for(i=0;i<n;i++)
88 {
89 list[0][i][0]=i+1;
90 list[0][i][1]=0;
91 }
92 if(bellman_ford()){printSolution();}
93 else printf("no solution.\n");
94 return 1;
95 }