注:章节序号与书中保持一致,省略部分为基础概念
4. 图与网络模型及方法
4.2 最短路问题
4.2.1 两个指定顶点之间的最短路径
Dijkstra 算法 function[mydistance,mypath]=mydijkstra(a,sb,db);
a
邻接矩阵a(i,j)
i-j
的距离,可以是有向的sb
起点的标号db
终点的标号mydistance
最短路距离mypath
最短路路径
4.2.3 每对顶点之间的最短路径
Dijkstra 算法是时间复杂度是 O ( n 3 ) O(n^3) O(n3),第二种解决该问题的方法是Floyd 算法
Matlab 实现
% floyd.m
% 采用floyd算法计算图a中每对顶点最短路
% d是矩离矩阵
% r是路由矩阵
function [d,r]=floyd(a)
n=size(a,1);
% 初始化距离矩阵
d=a;
% 初始化路由矩阵
for i=1:n
for j=1:n
r(i,j)=j;
end
end
r;
% Floyd算法开始
for k=1:n
for i=1:n
for j=1:n
if d(i,k)+d(k,j)<d(i,j)
d(i,j)=d(i,k)+d(k,j);
r(i,j)=r(i,k);
end
end
end
k;
d;
r;
end
d
r
4.3 最小生成树问题
实际问题举例: 把无向图的每个顶点看作村庄,计划修建道路使得可以在所有村庄之间通行。把每个村庄之间修建道路的费用看作权值,那么我们就可以得到一个求解修建道路的最小费用的问题。
4.3.1 最小生成树
最小生成树特点:
- 无回路,且包含原图中的n-1条边。
- 包含原图中的全部顶点。
- 边的权重和在所有其他生成树中最小。
- 最小生成树存在,则该图一定连通。反过来一样,图连通,则最小生成树一定存在。
prim 算法构造最小生成树
操作方法: 从某一顶点出发,逐步构建,让一棵小树逐渐长大。
Kruskal 算法构造最小生成树
操作方法: 此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。
4.4 网络最大流问题
实际问题举例: 将油从 s 运送到 t,中间的四个点为中转站,边的系数为管道容量。求 s 到 t 的最大流。
4.5 最小费用最大流问题
实际问题举例: 将油从 s 运送到 t,中间的四个点为中转站,边的系数为管道容量。求 s 到 t 的最大流。在该题干基础上,考虑不同管道上的运输费用也不相同,因为除了考虑输油管道的最大流外,还需要考虑输油管道输送最大流的最小费用。
4.6 Matlab 的图论工具箱
实例1: 求解 v 1 v_1 v1 到 v 1 1 v_11 v11 的最短路径及长度
clc,clear
a(1,2)=2;a(1,3)=8;a(1,4)=1;
a(2,3)=6;a(2,5)=1;
a(3,4)=7;a(3,5)=5;a(3,6)=1;a(3,7)=2;
a(4,7)=9;
a(5,6)=3;a(5,8)=2;a(5,9)=9;
a(6,7)=4;a(6,9)=6;
a(7,9)=3;a(7,10)=1;
a(8,9)=7;a(8,11)=9;
a(9,11)=2;a(9,10)=1;
a(10,11)=4;
a=a'; % matlab工具箱要求数据为下三角矩阵
[i,j,v]=find(a); % 找出a中非0元素的行和列,分别存储在i,j中,并将结果放在v中
b=sparse(i,j,v,11,11) % 构造稀疏矩阵
[x,y,z]=graphshortestpath(b,1,11,'Directed',false); % 声明是无向图'Directed',false(或写0)
[dist, path, pred]=graphshortestpath(G,S,T)
- G是稀疏矩阵,S是起点,T是终点。
- dist表示最短距离
- path表示最短距离经过的路径节点
- pred表示从S到每个节点的最短路径中,目标节点的先驱,即目标节点的前面一个节点。
实例2: (最短路算法)
- 用四维向量表示状态,(人,狼,羊,菜),在此岸时取1,在对岸时取0。
- 穷举所有可行状态,(1,1,1,1);(1,1,1,0) (1,1,0,1) (1,0,1,1) ;(1,0,1,0) (0,1,0,1);(0,0,1,0) (0,0,0,1) (0,0,0,0)
- 构造赋权图,顶点为以上10种可行状态,状态间可以转换时边对应权重为1,反正则为 ∞ ∞ ∞
- 问题转化为,初始点(1,1,1,1),结束点(0,0,0,0)的最短路径
本题难点在于邻接矩阵的表示,因为摆渡一次 就会改变现有的状态,为此再引入一个状态转移向量。
- 1表示过河,0表示未过河,状态转移有4种情况,人自己过河(1,0,0,0);人带狼(1,1,0,0);(1,0,1,0);(1,0,0,1)
- 状态向量 与 转移向量运算:0+0=0;1+0=1;0+1=1;1+1=0
- 如果可行状态+转移向量=可行状态,那么这两种可行状态对应的顶点就存在边。
% 4.10
clc,clear
% 可行状态行
a=[1 1 1 1;1 1 1 0;1 1 0 1;1 0 1 1; 1 0 1 0;0 1 0 1;0 1 0 0;0 0 1 0;0 0 0 1;0 0 0 0];
% 转移向量行
b=[1 0 0 0;1 1 0 0;1 0 1 0;1 0 0 1];
w=zeros(10); % 邻接矩阵初始化
for i=1:9 % 循环某顶点
for j=i+1:10 % 循环某顶点的下一个顶点
for k=1:4
% xor() 异或运算,参数同为0/不为0 返回0,参数有0有1时 返回1
if findstr(xor(a(i,:),b(k,:)),a(j,:)) % 判断初始顶点+状态转移 是否等于 下一个顶点
w(i,j)=1; % 是的话,初始顶点-下一个顶点的边权重=1
end
end
end
end
w=w'; % 下三角矩阵
c=sparse(w); % 构造稀疏矩阵
[x,y,z]=graphshortestpath(c,1,10,'Directed',0);
h=view(biograph(c,[],'ShowArrows','off','ShowWeights','off')); % 画出无向图
Edges=getedgesbynodeid(h); % 提取句柄h种的边集
set(Edged,'LineColor',[0 0 0]); % 边为黑色
set(Edged,'LineWidth',1.5); % 边宽度1.5
实例3: (注意是有向图)
clc,clear
a=zeros(7);
a(1,2)=4;a(1,3)=2;
a(2,3)=3;a(2,4)=2;a(2,5)=6;
a(3,4)=5;a(3,6)=4;
a(4,5)=2;a(4,6)=7;
a(5,6)=4;a(5,7)=8;