图与网络模型及方法
1.网络优化问题的例子
例 1 短路问题(SPP-shortest path problem)
一名货柜车司机奉命在短的时间内将一车货物从甲地运往乙地。从甲地到乙地的 公路网纵横交错,因此有多种行车路线,这名司机应选择哪条线路呢?假设货柜车的运行速度是恒定的,那么这一问题相当于需要找到一条从甲地到乙地的短路。
例 2 公路连接问题
公路连接问题 某一地区有若干个主要城市,现准备修建高速公路把这些城市连接起来,使得从其中任何一个城市都可以经高速公路直接或间接到达另一个城市。假定已经知道了任意两 个城市之间修建高速公路的成本,那么应如何决定在哪些城市间修建高速公路,使得总成本小?
例 3 指派问题(assignment problem)
一家公司经理准备安排N名员工去完成N 项任务,每人一项。由于各员工的特点 不同,不同的员工去完成同一项任务时所获得的回报是不同的。如何分配工作方案可以 使总回报大?
例 4 中国邮递员问题(CPP-chinese postman problem)
一名邮递员负责投递某个街区的邮件。如何为他(她)设计一条短的投递路线(从邮局出发,经过投递区内每条街道至少一次,后返回邮局)?由于这一问题是我国管梅谷教授 1960 年首先提出的,所以国际上称之为中国邮递员问题。
例 5 旅行商问题(TSP-traveling salesman problem)
一名推销员准备前往若干城市推销产品。如何为他(她)设计一条短的旅行路线 (从驻地出发,经过每个城市恰好一次,后返回驻地)?这一问题的研究历史十分悠 久,通常称之为旅行商问题。
例 6 运输问题(transportation problem)
某种原材料有M 个产地,现在需要将原材料从产地运往N 个使用这些原材料的工厂。假定M 个产地的产量和N 家工厂的需要量已知,单位产品从任一产地到任一工厂 的运费已知,那么如何安排运输方案可以使总运输成本低?
2.图的表示方法
2.1邻接矩阵法
同样,对于网络中的权,也可以用类似邻接矩阵的 n n× 矩阵表示。只是此时一条 弧所对应的元素不再是 1,而是相应的权而已。如果网络中每条弧赋有多种权,则可以 用多个矩阵表示这些权。
2.2关联矩阵表示法
例 8 对于例 7 所示的图,如果关联矩阵中每列对应弧的顺序为(1,2),(1,3),(2,4), (3,2),(4,3),(4,5),(5,3)和(5,4),则关联矩阵表示为
同样,对于网络中的权,也可以通过对关联矩阵的扩展来表示。例如,如果网络中 每条弧有一个权,我们可以把关联矩阵增加一行,把每一条弧所对应的权存储在增加的 行中。如果网络中每条弧赋有多个权,我们可以把关联矩阵增加相应的行数,把每一条 弧所对应的权存储在增加的行中。
2.3弧表表示法
弧表表示法将图以弧表(arc list)的形式存储在计算机中。所谓图的弧表,也就是 图的弧集合中的所有有序对。弧表表示法直接列出所有弧的起点和终点,共需 m 2 个存 储单元,因此当网络比较稀疏时比较方便。此外,对于网络图中每条弧上的权,也要对 应地用额外的存储单元表示。例如,例 7 所示的图,假设弧(1,2),(1,3),(2,4),(3,2), (4,3),(4,5),(5,3)和(5,4)上的权分别为 8,9,6,4,0,3,6 和 7,则弧表表示如表 1 所示。
2.4邻接表表示法
邻接表表示法将图以邻接表(adjacency lists)的形式存储在计算机中。所谓图的 邻接表,也就是图的所有节点的邻接表的集合;而对每个节点,它的邻接表就是它的所 有出弧。邻接表表示法就是对图的每个节点,用一个单向链表列出从该节点出发的所有 弧,链表中每个单元对应于一条出弧。为了记录弧上的权,链表中每个单元除列出弧的 另一个端点外,还可以包含弧上的权等作为数据域。图的整个邻接表可以用一个指针数 组表示。例如,例 7 所示的图,邻接表表示为
2.5星形表示法
2.5.1前向星形(forward star)表示法
例如,在例 7 所示的图中,仍然假设弧(1,2),(l,3) , (2,4), (3,2), (4,3) , (4,5), (5,3)和(5,4)上的权分别为 8,9,6,4,0,3,6 和 7。此时该网络图可以用前向 星形表示法表示为表 2 和表 3 。
在数组
p
o
i
n
t
point
point中,其元素个数比图的节点数多1(即
n
+
1
n+1
n+1),且一定有
p
o
i
n
t
(
1
)
=
1
point(1)=1
point(1)=1,
p
o
i
n
t
(
n
+
1
)
=
m
+
1
point(n+1)=m+1
point(n+1)=m+1。对于节点
i
i
i,其对应的出弧存放在弧信息数组位置区间为
[
p
o
i
n
t
(
i
)
,
p
o
i
n
t
(
i
+
1
)
−
1
]
[point(i),point(i+1)-1]
[point(i),point(i+1)−1],如果
p
o
i
n
t
(
i
)
=
p
o
i
n
t
(
i
+
1
)
point(i)=point(i+1)
point(i)=point(i+1),则节点
i
i
i没有出弧。
2.5.2后向星形(forward star)表示法
对于网络图的表示法,我们作如下说明:
① 星形表示法和邻接表表示法在实际算法实现中都是经常采用的。星形表示法的优点是占用的存储空间较少,并且对那些不提供指针类型的语言(如FORTRAN 语言 等)也容易实现。邻接表表示法对那些提供指针类型的语言(如 C 语言等)是方便的, 且增加或删除一条弧所需的计算工作量很少,而这一操作在星形表示法中所需的计算工 作量较大(需要花费 O(m) 的计算时间)。 有关“计算时间”的观念是网络优化中需要 考虑的一个关键因素。
② 当网络不是简单图,而是具有平行弧(即多重弧)时,显然此时邻接矩阵表示 法是不能采用的。其他方法则可以很方便地推广到可以处理平行弧的情形.
③ 上述方法可以很方便地推广到可以处理无向图的情形,但由于无向图中边没有 方向,因此可能需要做一些自然的修改。例如,可以在计算机中只存储邻接矩阵的一半 信息(如上三角部分),因为此时邻接矩阵是对称矩阵。无向图的关联矩阵只含有元素 0 和 +1 ,而不含有 -1 ,因为此时不区分边的起点和终点。又如,在邻接表和星形表示法中,每条边会被存储两次,而且反向星形表示显然是没有必要的,等等。
2.7轨与连通
W = v 0 e 1 v 1 e 2 . . . e k v k , 其 中 e i ∈ E ( G ) , 1 < i < k , v j ∈ V ( G ) , 0 ≤ j ≤ k , e i 与 v i − 1 , v i 关 联 , 称 W 是 图 G 的 一 条 道 路 , k 为 路 长 , 顶 点 v 0 和 v k 分 别 称 为 W 的 起 点 和 终 点 , 而 v 1 , v 2 , . . . , v k + 1 称 为 它 的 内 部 顶 点 。 W=v_0e_1v_1e_2...e_kv_k,其中e_i∈E(G),1<i<k,v_j∈V(G),0≤j≤k,e_i与v_{i-1},v_i关联,称W是图G的一条道路,k为路长,顶点v_0和v_k分别称为W的起点和终点,而v_1,v_2,...,v{k+1}称为它的内部顶点。 W=v0e1v1e2...ekvk,其中ei∈E(G),1<i<k,vj∈V(G),0≤j≤k,ei与vi−1,vi关联,称W是图G的一条道路,k为路长,顶点v0和vk分别称为W的起点和终点,而v1,v2,...,vk+1称为它的内部顶点。
应用——最短路问题
3.1 两个指定顶点之间的短路径
例9
clc,clear
a=zeros(6);
a(1,2)=50;a(1,4)=40;a(1,5)=25;a(1,6)=10;
a(2,3)=15;a(2,4)=20;a(2,6)=25;
a(3,4)=10;a(3,5)=20;
a(4,5)=10;a(4,6)=25;
a(5,6)=55;
a=a+a'; %把三角矩阵对称过来,成为6×6的对称矩阵
a(find(a==0))=inf; %找到数值为0的位置把数值替换成inf
pb(1:length(a))=0;%创建一个1行6列的零矩阵
pb(1)=1;%矩阵的第一个元素赋值为1
index1=1;%标号顶点顺序,从第一个点出发
index2=ones(1,length(a));%标号顶点索引index2=[1 1 1 1 1 1],
%index2存放始点到第i点最短通路中第i顶点前一项点的序号
d(1:length(a))=inf;d(1)=0;%d(i)存放由始点到第i点最短通路的值
temp=1;
while sum(pb)<length(a) %没走遍六个地方之前都要循环
tb=find(pb==0); %tb里面存的是pb=0的位置,即没经过的位置
d(tb)=min(d(tb),d(temp)+a(temp,tb)); %迪克斯特拉算法算第一站到各个站的成本
%在d(tb)的成本:如果计算结果小于当前值就更新成本
tmpb=find(d(tb)==min(d(tb))); % 找到成本d(tb)最少的时候的值所在的位置
temp=tb(tmpb(1));% 找到所在的位置
pb(temp)=1; %将这个位置标记为1,说明走过了
index1=[index1,temp]; %存下走过的路径
temp2=find(d(index1)==d(temp)-a(temp,index1));
index2(temp)=index1(temp2(1));
end
d, index1, index2
我们编写的从起点sb到终点db通用的Dijkstra标号算法程序如下:
function [mydistance,mypath]=mydijkstra(a,sb,db);
% 输入:a—邻接矩阵(aij)是指i到j之间的距离,可以是有向的
% sb—起点的标号, db—终点的标号
% 输出:mydistance—短路的距离, mypath—短路的路径
n=6;
n=size(a,1); visited(1:n) = 0;
distance(1:n) = inf; % 保存起点到各顶点的短距离
distance(sb) = 0; parent(1:n) = 0;
for i = 1: n-1
temp=distance;
id1=find(visited==1); %查找已经标号的点
temp(id1)=inf; %已标号点的距离换成无穷
[t, u] = min(temp); %找标号值小的顶点
visited(u) = 1; %标记已经标号的顶点
id2=find(visited==0); %查找未标号的顶点
for v = id2
if a(u, v) + distance(u) < distance(v)
distance(v) = distance(u) + a(u, v); %修改标号值
parent(v) = u;
end
end
end
mypath = [];
if parent(db) ~= 0 %如果存在路!
t = db; mypath = [db];
while t ~= sb
p = parent(t);
mypath = [p mypath];
t = p;
end
end
mydistance = distance(db);
return
3.2两个指定顶点之间最短路问题的数学表达式
model:
sets:
cities/A,B1,B2,C1,C2,C3,D/;
roads(cities,cities)/A B1,A B2,B1 C1,B1 C2,B1 C3,B2 C1, B2 C2,B2 C3,C1 D,C2 D,C3 D/:w,x;
endsets
data:
w=2 4 3 3 1 2 3 1 1 3 4;
enddata
n=@size(cities);!城市的个数;
min=@sum(roads:w*x);
@for(cities(i)|i#ne#1 #and# i #ne#n:
@sum(roads(i,j):x(i,j))=@sum(roads(j,i):x(j,i)));
@sum(roads(i,j)|i #eq#1:x(i,j))=1;
@sum(roads(i,j)|j #eq#n:x(i,j))=1;
end
model:
sets: cities/1..11/;
roads(cities,cities):w,x;
endsets
data:
w=0;
enddata
calc:
w(1,2)=2;w(1,3)=8;w(1,4)=1;
w(2,3)=6;w(2,5)=1;
w(3,4)=7;w(3,5)=5;w(3,6)=1;w(3,7)=2;
w(4,7)=9;
w(5,6)=3;w(5,8)=2;w(5,9)=9;
w(6,7)=4;w(6,9)=6;
w(7,9)=3;w(7,10)=1;
w(8,9)=7;w(8,11)=9;
w(9,10)=1;w(9,11)=2;w(10,11)=4;
@for(roads(i,j):w(i,j)=w(i,j)+w(j,i));
@for(roads(i,j):w(i,j)=@if(w(i,j) #eq# 0, 1000,w(i,j)));
endcalc
n=@size(cities); !城市的个数;
min=@sum(roads:w*x);
@for(cities(i)|i #ne#1 #and# i #ne#
n:@sum(cities(j):x(i,j))=@sum(cities(j):x(j,i)));
@sum(cities(j):x(1,j))=1;
@sum(cities(j):x(j,1))=0; !不能回到顶点1;
@sum(cities(j):x(j,n))=1;
@for(roads:@bin(x));
end