数学建模图论模型图论文档格式.docx
《数学建模图论模型图论文档格式.docx》由会员分享,可在线阅读,更多相关《数学建模图论模型图论文档格式.docx(32页珍藏版)》请在冰点文库上搜索。
1
2
3
4
5
6
7
8
9
10
EV[k].p1
EV[k].p2
COST[EV[k].p1,EV[k].p2]
11
14
18
19
21
27
33
按权值由小到大选取各边就是在数组中按下标k由1到en(图中边数)的次序选取。
选前2条边(2,3),(2,4)时均无问题,保留作为树的边;
到第3条边(4,3)时将与已保留的边形成回路,将其舍去;
同样继续做:
保留(2,6);
舍去(6,4);
保留(5,7),(1,5),(1,6),此时,保留的边数已够(n-1)=6条边,此时必定将7个顶点全部互相连通了,后面剩下的边(1,2),(5,6)就不必再考虑了。
最后得到的最小生成树如图2a中深色边所示,其各边权值总和等于80。
由离散数学中的图论可以证明,这就是最小生成树了,其权值最小。
当图中有权值相等的边时,其最小生成树可能有不同的选取方案。
实现此算法的关键是,在选取某条边时应判断是否与已保留的边形成回路。
这可用将各顶点划分为集合的办法解决:
假设数组tag(1..en)作为顶点集合划分的标志初值为0。
在算法的执行过程中,当所选顶点u,v是连通的,则将相应位置的tag[u],tag[v]置以相同的数字,而不连通的点在初期分属不同的集合,置不同的数字;
一旦两个不同的连通分支连通了,则修改tag的值,将新的连通分支改为相同的数字。
我们以图2为例。
首先选(2,3)(2,4)边,由于是连通的,并且不出现回路。
tag[2]:
=1,tag[3]:
=tag[4]:
=1是同一个集合A;
选(6,2)边与A集合连通;
tag[6]:
=1;
再选(5,7)与集合A不连通,tag[5]:
=tag[7]:
=2构成另一集合B;
选(1,5)边与集合B连通,tag[1]:
=2;
此时,集合A={2,3,4,6};
集合B={5,7,1};
当选(1,6)边时,(1,6)与集合A、集合B都连通,并且两个顶点分别属于两个不同的集合A、B,这使得集合A与集合B通过边(1,6)连通。
修改集合B中tag的值,置为1,即将集合B并入集合A。
边为n-1条,这就是一棵最小生成树。
根据集合标志数组tag的变化过程,很容易判断,选择一条新的边是否构成回路。
当新选边的两个顶点u、v,若tag[u]和tag[v]相同并且均不等于0时,即u,v已在生成树集合中被保留过,加入u,v后即形成回路,不能选。
而当tag[u]<
>
tag[v]或者tag[u]=tag[v]=0时,可以选并且不形成回路,说明u,v中至少有一个顶点未被选过或者被选过的u、v分别属于两个不同的集合,此时选择u,v可以将含u的集合与含v的集合连通,修改tag数组。
如此下去,到所有顶点均已属于一个集合时,此最小生成树就完全构成了。
网的最小生成树算法描述如下:
假设算法中用到的数据结构是经过处理的。
COST(1..n,1..n)是带权数组存放网中顶点之间的权。
EV(1..n*(n-1/2))按权从小到大存放排序后的顶点对,即EV[K].P1存放一个顶点,边的另一顶点存放在EV[K].P2之中。
tag(1..n):
顶点集合划分标志的数组。
Enumb:
当前生成树的边数。
SM:
当前权累计和。
PROCminspanningtree(VARcost;
VARev);
Vartag;
BEGIN
CALLINITIAL(tag);
=0;
SM:
=0;
{诸参量初始化}
k:
=1;
{边数累计}
WHILE(Enumb<
=n-1)AND(k<
=n)DO
BeginU:
=EV[k].P1;
V:
=EV[k].P2;
{选一对顶点(U,V)}
CALLFIND(U,T);
{找到含顶点U的集合T}
CALLFIND(V,W);
{找到含顶点V的集合W}
IF(T<
W)THEN
Begin
write(u,v);
Enumb:
=Enumb+1;
{最小生成树增加一条边}
SM:
=SM+COST[u,v];
MERGE(T,W);
{选u,v不会形成环,合并T,W集合,并修改tag}
end
K:
=K+1;
{找下一条边}
IFEnumb<
n-1THENwrite('
Thereisnotaminspanningtree'
)
ELSEwrite(SM)
END;
由算法可知图2的最小生成树的结果是(2,3),(2,4),(2,6),(5,7),(1,5),(1,6)。
2最短路径
在一个赋权有向图上寻找最短路径问题也是图应用的一个重要课题。
假定图3中的有向图G=(V,E)是一个航空图,V的每一顶点表示—个城市,正中的每条弧v—>
w表示从城市v到城市W的航线,弧V—>
w上的标号代表从V城飞到w城所需要的时间。
要寻找由该航空图上一给定城市到另一城市所需要的最短飞行时间。
可以用求解这个有向图的单源最短路径算法来完成。
下面,我们讨论求解单源最短路径问题的贪心算法,也称Dijkstra算法。
设有向图G=(V,E),其中,V={1,2,…,n).cost是表示G的邻接矩阵,cost[i,j]表示有向边(i,j)的权。
若不存在有向边(i,j),则cost[i,j]的权为无限大(oo)。
令S是一个集合,其中的每个元素表示一个顶点,从源点到这些顶点的最短距离已经求出。
(1)令顶点V0为源点,集合S的初态只包含顶点V0,即S={V0}。
数组dist记录从源点到其他各顶点当前的最短距离,其初值为dist[i]:
=cost[v0,i],(i=2,…,n)。
(2)从S之外的顶点集合V-S中选出一个顶点W,使dist[W]的值最小。
于是,从源点到达W只通过S中的顶点,我们把W加入集合S。
(3)调整dist中记录的从源点到V-S中每个顶点V的距离:
从原来的dist[v]和dist[w]+cost[w,v]中选择较小的值作为新的dist[v]。
(4)重复上述过程
(2)和(3),直到S中包含V的全部顶点。
最终数组dist记录了从源点到V中其余各顶点的最短路径。
对图3所示的加权有向图应用Dijkstra算法,从源点V2出发到达各顶点的最短路径如下表所示。
最短路径
---------------------------------------
源点中间顶点终止顶点长度
2510
315
3430
3135
6oo
--------------------------------------
对图3的执行过程:
初始时,S={2},dist[1]=oo,dist[3]=15,dist[4]=oo,dist[5]=10,dist[6]=oo,第一遍处理时,W=2使dist[5]最小、于是把5加入S。
然后,调整dist中从源点到其余各顶点的距离:
dist[3]=15,为次小,将3加入S。
dist[4]=cost[2,3]+cost[3,4]=15+15=30,经中间点3。
S={2,5,3,4},同理,dist[1]=cost[2,3]+cost[3,1]=35,S={2,5,3,4,1},由于2没有一条到6的路径,所以dist[6]=oo。
由此我们给出最短路径算法如下
PROCshortpath(VARcost;
VARdist;
VARpath;
VARS,V0);
BEGIN
FORW:
=1TOnDO
Begindist[W]:
=cost[V0,W];
{最短路径初始化值}
IFcost[V0,W]<
max
THENpath[W]:
=V0;
{path记载当前最短路径}
End;
S:
=[V0];
Vnum:
{到达点集合S和到达点S个数初值}
WHILE(Vnum<
n-1)DO{最后一点已无选择余地}
BeginWm:
=max;
u:
=V0;
IF(NOTWINS)AND(dist[W]<
Wm)
THENBeginU:
=W;
Wm:
=dist[w]End;
{找最小dist[w]}
=S+[U];
=Vnum+1;
{U为找到最短路径的终点}
IF(NOTWINS)AND(dist[U]+cost[U,W]<
dist[W])
THENBegindist[W]:
=dist[U]+cost[U,W];
{调整非S集各点最短路径值}
path[W]:
=U;
{调整非S集各点最短路径}
Vnum:
=Vnum+1
END;
PROCPRINTPATH(VARdistVARpath;
VARS;
V0)
FORi:
IF(iINS)
THENBegink:
=i;
WHILE(k<
V0)DO
Beginwrite(k);
k:
=path[k]End;
{通过找前趋点,反向输出最短路径}
write(k);
writeln(dist[i])
ELSEBeginwrite(i,V0);
writeln('
max'
)End;
容易看出,算法shortpath的时间复杂度为O(n2),空间复杂度为O(n)。
3拓扑排序
本节说明了如何用,对一个有向无回路图进行拓扑排序。
有向无回路图又称为dag。
对这种有向无回路图的拓扑排序的结果为该图所有顶点的一个线性序列,满足如果G包含(u,v),则在序列中u出现在v之前(如果图是有回路的就不可能存在这样的线性序列)。
一个图的拓扑排序可以看成是图的所有顶点沿水平线排成的一个序列,使得所有的有向边均从左指向右。
因此,拓扑排序不同于通常意义上对于线性表的排序。
有向无回路图经常用于说明事件发生的先后次序,给出一个实例说明早晨穿衣的过程。
必须先穿某一衣物才能再穿其他衣物(如先穿袜子后穿鞋),也有一些衣物可以按任意次序穿戴(如袜子和短裤)。
图1(a)所示的图中的有向边(u,v)表明衣服u必须先于衣服v穿戴。
因此该图的拓扑排序给出了一个穿衣的顺序。
每个顶点旁标的是发现时刻与完成时刻。
图1(b)说明对该图进行拓扑排序后将沿水平线方向形成一个顶点序列,使得图中所有有向边均从左指向右。
下列简单算法可以对一个有向无回路图进行拓扑排序。
procedureTopological_Sort(G);
begin
1.计算每个顶点的完成时间f[v];
2.当每个顶点完成后,把它插入链表前端;
3.返回由顶点组成的链表;
end;
图1(b)说明经拓扑排序的结点以与其完成时刻相反的顺序出现。
因为深度优先搜索的运行时间为θ(V+E),每一个v中结点插入链表需占用的时间为θ
(1),因此进行拓扑排序的运行时间θ(V+E)。
图1早晨穿衣的过程
为了证明算法的正确性,我们运用了下面有关有向无回路图的重要引理。
引理1
有向图G无回路当且仅当对G进行深度优先搜索没有得到反向边。
证明:
→:
假设有一条反向边(u,v),那么在深度优先森林中结点v必为结点u的祖先,因此G中从v到u必存在一通路,这一通路和边(u,v)构成一个回路。
←:
假设G中包含一回路C,我们证明对G的深度优先搜索将产生一条反向边。
设v是回路C中第一个被发现的结点且边(u,v)是C中的优先边,在时刻d[v]从v到u存在一条由白色结点组成的通路,根据可知在深度优先森林中结点u必是结点v的后裔,因而(u,v)是一条反向边。
(证毕)
定理1
Topological_Sort(G)算法可产生有向无回路图G的拓扑排序。
证明:
假设对一已知有问无回路图G=(V,E)运行过程DFS以确定其结点的完成时刻。
那么只要证明对任一对不同结点u,v∈V,若G中存在一条从u到v的有向边,则f[v]<
f[u]即可。
考虑过程DFS(G)所探寻的任何边(u,v),当探寻到该边时,结点v不可能为灰色,否则v将成为u的祖先,(u,v)将是一条反向边,和引理1矛盾。
因此,v必定是白色或黑色结点。
若v是白色,它就成为u的后裔,因此f[v]<
f[u]。
若v是黑色,同样f[v]<
这样一来对于图中任意边(u,v),都有f[v]<
f[u],从而定理得证。
另一种拓扑排序的算法基于以下思想:
首先选择一个无前驱的顶点(即入度为0的顶点,图中至少应有一个这样的顶点,否则肯定存在回路),然后从图中移去该顶点以及由他发出的所有有向边,如果图中还存在无前驱的顶点,则重复上述操作,直到操作无法进行。
如果图不为空,说明图中存在回路,无法进行拓扑排序;
否则移出的顶点的顺序就是对该图的一个拓扑排序。
下面是该算法的具体实现:
procedureTopological_Sort_II(G);
begin
1for每个顶点u∈V[G]dod[u]←0;
//初始化d[u],d[u]用来记录顶点u的入度
2for每个顶点u∈V[G]do
3for每个顶点v∈Adj[u]dod[v]←d[v]+1;
//统计每个顶点的入度
4CreateStack(s);
//建立一个堆栈s
5for每个顶点u∈V[G]do
6ifd[u]=0thenpush(u,s);
//将度为0的顶点压入堆栈
7count←0;
8while(notEmpty(s))do
begin
9u←top(s);
//取出栈顶元素
10pop(s);
//弹出一个栈顶元素
11count←count+1;
12R[count]←u;
//线性表R用来记录拓扑排序的结果
13for每个顶点v∈Adj[u]do//对于每个和u相邻的节点v
14d[v]←d[v]-1;
15ifd[v]=0thenpush(v,s);
//如果出现入度为0的顶点将其压入栈
end;
end;
16ifcount<
G.sizethenwriteln('
Error!
Thegraphhascycle.'
)
17else按次序输出R;
上面的算法中利用d[u]来记录顶点u的入度,第2-3行用来统计所有顶点的入度,第5-6行将入度为0的顶点压入堆栈,第8-15行不断地从栈顶取出顶点,将该顶点输出到拓扑序列中,并将所有与该顶点相邻的顶点的入度减1,如果某个顶点的入度减至0,则压入堆栈,重复该过程直到堆栈空了为止。
显而易见该算法的复杂度为O(VE),因为第2-3行的复杂性就是O(VE),后面8-15行的复杂性也是O(VE)。
这个算法虽然简单,但是没有的效率高。
4网络流算法:
概念
在实际生活中有许多流量问题,例如在交通运输网络中的人流、车流、货物流,供水网络中的水流,金融系统中的现金流,通讯系统中的信息流,等等。
50年代以福特(Ford)、富克逊(Fulkerson)为代表建立的“网络流理论”,是网络应用的重要组成部分。
在最近的奥林匹克信息学竞赛中,利用网络流算法高效地解决问题已不是什么稀罕的事了。
本节着重介绍最大流(包括最小费用)算法,并通过实际例子,讨论如何在问题的原型上建立—个网络流模型,然后用最大流算法高效地解决问题。
[问题描述]如图4-1所示是联结某产品地v1和销售地v4的交通网,每一弧(vi,vj)代表从vi到vj的运输线,产品经这条弧由vi输送到vj,弧旁的数表示这条运输线的最大通过能力。
产品经过交通网从v1到v4。
现在要求制定一个运输方案使从v1到v4的产品数量最多。
图4-1
图4-2
一、基本概念及相关定理
1)网络与网络流
定义1给一个有向图N=(V,E),在V中指定一点,称为源点(记为vs,和另一点,称为汇点(记为vt),其余的点叫中间点,
对于E中每条弧(vi,vj)都对应一个正整数c(vi,vj)≥O(或简写成cij),称为f的容量,则赋权有向图N=(V,E,c,vs,vt)称为一个网络。
如图4-1所给出的一个赋权有向图N就是一个网络,指定v1是源点,v4为汇点,弧旁的数字为cij。
所谓网络上的流,是指定义在弧集合E上一个函数f={f(vi,vj)},并称f(vi,vj)为弧(vi,vj)上的流量(下面简记为fij)。
如图4-2所示的网络N,弧上两个数,第一个数表示容量cij,第二个数表示流量fij。
2)可行流与最大流
在运输网络的实际问题中,我们可以看出,对于流有两个显然的要求:
一是每个弧上的流量不能超过该弧的最大通过能力(即弧的容量);
二是中间点的流量为0,源点的净流出量和汇点的净流入量必相等且为这个方案的总输送量。
因此有:
定义2满足下列条件
(1)容量约束:
0≤fij≤cij,(vi,vj)∈E,
(2)守恒条件
对于中间点:
流入量=流出量;
对于源点与汇点:
源点的净流出量vs(f)=汇点的净流入量(-vt(f))
的流f,称为网络N上的可行流,并将源点s的净流量称为流f的流值v(f)。
网络N中流值最大的流f*称为N的最大流。
3)可增广路径
所谓可增广路径,是指这条路径上的流可以修改,通过修改,使得整个网络的流值增大。
定义3设f是一个可行流,P是从源点s到汇点t的一条路,若p满足下列条件:
(1)在p上的所有前向弧(vi→vj)都是非饱和弧,即0≤fij<
cij
(2)在p上的所有后向弧(vi←vj)都是非零弧,即0<
fij≤cij
则称p为(关于可行流f的)一条可增广路径。
(4)割及其容量
定义4如果A是V的一个子集,A-=V-A,s∈A,t∈A-,则称边集(A,A-)为网络N的一个割,显然,若把某一割的弧从网络中丢去,则从vs到vt就不存在路。
所以直观上讲,割是从vi到vj的必经之道。
定义5给一割(A,A-),把其中所有弧的容量之和称为这个割的容量,记为c(A,A-),即
c(A,A-)=∑c(e)
网络N中容量最小的割(A*,A*-)称为N的最小割。
不难证明,任何一个可行流的流量v(f)都不会超过任一割的容量,即
v(f)≤c(A,A-)
例如,图4-2中,若A={s},(A,A-)={(s,v3),(s,v2)},c(A,A-)=4+3=7。
(5)有关定理
定理1当且仅当不存在关于f*的增广路径,可行流f*为最大流。
证明必要性:
若f*是最大流,设N中存在关于f*的增广路径p,令:
Q=min{min(cij-fij),minf*ij}
由增广路径定义可知,Q>
0,再令:
f**ij=
f*ij+Q
(vi,vj)∈P的前向弧的集合
f*ij-Q
(vi,vj)∈P的后向弧的集合
f*ij
(vi,vj)不属于P的集合
不难证明{f**ij}是—可行流,且v(f**)=v(f*)+Q>
v(f*)。
这与f*是最大流假设矛盾,必要性证毕。
证明充分性:
设N中不存在关于f*的增广路径,证明f*是最大流。
我们利用下面的方法来定义A*。
令vs∈A*
若vi∈A*,且fij<
cij,则令vj∈A*;
若vi∈A*,且fji>
0,则令vj∈A*。
因为不存在关于f*的增广路径,故vt不属于A*。
记A*-=V-A*,于是得到一个割(A*,A*-),显然有
f*ij=cij,(vi,vj)∈(A*,A*-)
f*ij=0,(vi,vj)∈(A*-,A*)
所以v(f*)=c(A*,A*-)。
于是f*必是最大流,定理得证。
由上述证明中可得,若f*是最大流,则网络中必存在一个割集c(A*,A*-),使