第四章:图与网络

4.1 基本概念及数据结构

4.4.1 基本概念

混合图:有的边有方向,有的边无方向
简单图:任两顶点间最多有一条边,且每条边的两个端点皆不重合。
两顶点相邻:图的两顶点间有边相连
完全图:每一对顶点都相邻
二分图:能把顶点分成两个非空子集X,Y,且X,Y中均无相邻顶点对
设v ∈ \in V是边e ∈ \in E的端点,则称v与e相关联,与顶点v关联的边数称为该顶点的度,记为d(v)
赋权图:每条边都有一个(或多个)实数对应

4.4.2 数据结构

邻接矩阵表示法:
y = { 权 值 ( 1 , 无 权 值 ) , 当 两 点 之 间 有 边 时 0 或 ∞ , 当 两 点 之 间 无 边 时 y =\begin{cases} 权值(1,无权值),当两点之间有边时\\ 0或\infty,当两点之间无边时\end{cases} y={10
稀疏矩阵表示法:
(非零元素的行地址,非零元素的列地址),非零元素的值
互相转化(matlab):
普通矩阵用sparse变为稀疏矩阵
稀疏矩阵用full变为普通矩阵

4.2 最短路问题

4.2.1 两个指定顶点之间

迪杰斯特拉(Dijkstra)算法:按距u从近到远为顺序,一次求得u到G的各顶点的最短路和距离,直至v(或直至G的所有顶点),算法结束。
例程:
有如下矩阵:
在这里插入图片描述

%求第一个点到其他点的最短路径
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';
a(a==0)=inf;
pb(1:length(a))=0;pb(1)=1;index1=1;index2=ones(1,length(a));
d(1:length(a))=inf;d(1)=0;
temp=1; %最新的P标号的顶点
while sum(pb)<length(a)
   tb=find(pb==0);
   d(tb)=min(d(tb),d(temp)+a(temp,tb));
   tmpb=find(d(tb)==min(d(tb)));
   temp=tb(tmpb(1)); %可能有多个点同时达到最小值,只取其中的一个
   pb(temp)=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-邻接矩阵,a(i,j)-到j之间的距离,可以是有向的
%sb-起点的标号,db-终点的标号
%输出:mydistance-最短路的距离,mypath-最短路的路径
n=size(a,1); visited(1:n) = 0;
distance(1:n) = inf; distance(sb) = 0; %起点到各顶点距离的初始化
visited(sb)=1; u=sb;  %u为最新的P标号顶点
parent(1:n) = 0; %前驱顶点的初始化
for i = 1: n-1
     id=find(visited==0); %查找未标号的顶点
     for v = id           
         if  a(u, v) + distance(u) < distance(v)
             distance(v) = distance(u) + a(u, v);  %修改标号值
             parent(v) = u;                                    
         end            
     end
     temp=distance;
     temp(visited==1)=inf;  %已标号点的距离换成无穷
     [t, u] = min(temp);  %找标号值最小的顶点
     visited(u) = 1;       %标记已经标号的顶点
 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);
4.4.2 每对顶点之间

Floyd算法:
解上图的例程:

clear;clc;
n=6; a=zeros(n);
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'; 
a(a==0)=inf; %把所有零元素替换成无穷
a([1:n+1:n^2])=0; %对角线元素替换成零,Matlab中的数据是逐列存储的
path=zeros(n);
for k=1:n
   for i=1:n
      for j=1:n
         if a(i,j)>a(i,k)+a(k,j)
            a(i,j)=a(i,k)+a(k,j);
            path(i,j)=k;
         end 
      end
   end
end
a, path

从起点sb到终点db通用的Floyd算法:

function [dist,mypath]=myfloyd(a,sb,db);
%输入:a-邻接矩阵,a(i,j)-到j之间的距离,可以是有向的
%sb-起点的标号,db-终点的标号
%输出:dist-最短路的距离,mypath-最短路的路径
n=size(a,1); path=zeros(n);
for k=1:n
    for i=1:n
        for j=1:n
            if a(i,j)>a(i,k)+a(k,j)
                a(i,j)=a(i,k)+a(k,j);
                path(i,j)=k;
            end
        end
    end
end
dist=a(sb,db);
parent=path(sb,:); %从起点sb到终点db的最短路上各顶点的前驱顶点
parent(parent==0)=sb; %path中的分量为0,表示该顶点的前驱是起点
mypath=db; t=db;
while t~=sb
        p=parent(t); mypath=[p,mypath];
        t=p;
end

4.3 最小生成树问题

对于树和图的相关概念此处不予详解,不太懂的可以查阅相关资料进一步了解。这里主要对求解最小生成树的过程进行解析。

4.3.1 prim算法(联通赋权图)

设置两个集合P和Q,其中P用于存放G的最小生成树中的顶点,集合Q存放G的最小生成树中的边。假设构造最小生成树时,从顶点v1出发。

  1. P = { v1 },Q = ∅ \emptyset
  2. while P ~= V
  3. 找最小边pv,其中p ∈ \in P,v ∈ \in V - P;
  4. P = P + { v };Q = Q + { pv };
  5. end

例图:
在这里插入图片描述
例程:

clc;clear;
a=zeros(7);
a(1,2)=50; a(1,3)=60;
a(2,4)=65; a(2,5)=40;
a(3,4)=52;a(3,7)=45;
a(4,5)=50; a(4,6)=30;a(4,7)=42;
a(5,6)=70; 
a=a+a';%a'是a的转置
a(a==0)=inf;
result=[];p=1;tb=2:length(a);
while size(result,2)~=length(a)-1
   temp=a(p,tb);temp=temp(:);
   d=min(temp);
   [jb,kb]=find(a(p,tb)==d,1); %找第1个最小值
   j=p(jb);k=tb(kb);
   result=[result,[j;k;d]];p=[p,k];tb(find(tb==k))=[];
end
result
4.3.2 Kruskal算法
  1. 选e1 ∈ \in E(G),是的e1是权值的最小边

  2. 若e1,e2,…,ei已选好,则从
    E(G) - { e1,e2,…,ei }中选取ei+1,使得
    (1){ e1,e2,…,ei,ei+1 }中无圈
    (2)ei+1是E(G) - { e1,e2,…,ei }中权值最小的边

  3. 直到选得e|V|-1为止

例程:

clc;clear;
a(1,[2,3])=[50,60]; a(2,[4,5])=[65,40]; 
%这里给出邻接矩阵的另外一种输入方式
a(3,[4,7])=[52,45]; a(4,[5,6])=[50,30];
a(4,7)=42; a(5,6)=70; 
[i,j,b]=find(a);
data=[i';j';b'];index=data(1:2,:);
loop=length(a)-1;
result=[];
while length(result)<loop
   temp=min(data(3,:));
   flag=find(data(3,:)==temp);
   flag=flag(1);
   v1=index(1,flag);v2=index(2,flag);
   if v1~=v2
      result=[result,data(:,flag)];
   end
   index(find(index==v2))=v1;
   data(:,flag)=[];
   index(:,flag)=[];
end
result

4.4 网络最大流

不明白基础概念的需要先了解
寻求最大流的标号法(Ford-Fullkerson
1.标号过程
每个顶点的标号值有两个:
第一个标号值表示在可能的增广路上,它的前驱顶点;
第二个标号值 δ \delta δ表示唉可能的增广路上可以调整的流量

  1. 初始化,给发点 v s v_s vs,标号为(0, ∞ \infty

  2. 若顶点 v x v_x vx已经标号,则对 v x v_x vx的所有未标号的临接顶点 v y v_y vy按以下规则标号:
    在这里插入图片描述

  3. 不断重复步骤2知道收点 v t v_t vt被标号,或不再有顶点可以标号为止。当 v t v_t vt被标号时,表明存在一条从 v s v_s vs v t v_t vt的增广路,则转向则增流过程。如若 v t v_t vt点不能被标号,且不存在其他可以标号的顶点时,表明不存在从 v s v_s vs v t v_t vt的增广路,算法结束,此时所获得的流就是最大流

2.增流过程
在这里插入图片描述
最大流算法的实现可以使用MATLAB工具箱

4.5 最小费用最大流

最小费用最大流问题就是求一个从发点 v s v_s vs到收点 v t v_t vt 的最大流,是流的总输送费用取最小值。
此处书中只有lingo程序,便不再做详解,MATLAB均可用工具箱来解决

4.6 Matlab的图论工具箱

在这里插入图片描述

最短路

在这里插入图片描述

clc, clear
a(1,2)=2;a(1,3)=8;a(1,4)=1;
a(2,3)=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,10)=1;a(9,11)=2;
a(10,11)=4;
a=a';   %matlab工具箱要求数据是下三角矩阵
[i,j,v]=find(a);
b=sparse(i,j,v,11,11) %构造稀疏矩阵
[x,y,z]=graphshortestpath(b,1,11,'Directed',false) 
%Directed是标志图为有向或无向的属性,该图是无向图,对应的属性值为false(也可以写作0)
h = view(biograph(b,[],'ShowArrows','off','ShowWeights','off'));
Edges = getedgesbynodeid(h); %提取句柄h中的边集
set(Edges,'LineColor',[0 0 0]); %为了打印清楚,边画成黑色
set(Edges,'LineWidth',1.5);  %线性宽度设置为5
最小生成树
clc, clear
x=[0     5    16    20    33    23    35    25    10];
y=[15    20    24    20    25    11     7     0     3];
xy=[x;y];
d=mandist(xy); %求xy的两两列向量间的绝对值距离
d=tril(d); %截取MATLAB工具箱要求的下三角矩阵
b=sparse(d) %化为系数矩阵
[ST,pred]=graphminspantree(b,'Method','Kruskal')  %调用最小生成树的命令
st=full(ST); %把最小生成树的稀疏矩阵转化为普通矩阵
TreeLength=sum(sum(st))  %求最小生成树的长度
view(biograph(ST,[],'ShowArrows','off')) %画出最小生成树
最大流

只能解决权重都为正值,且两个顶点之间不能有两条弧的问题,需要做相应变换,增加虚拟节点。

clc, clear, a=zeros(9);
a(1,2)=6; a(1,3)=4; a(1,4)=5;
a(2,3)=3; a(2,5)=9; a(2,6)=9;
a(3,4)=4; a(3,5)=6; a(3,6)=7; a(3,7)=3;
a(4,7)=5; a(4,9)=2;
a(5,8)=12;
a(6,5)=8; a(6,8)=10;
a(7,6)=4; a(7,8)=15; 
a(9,3)=2;
b=sparse(a);
[x,y,z]=graphmaxflow(b,1,8)

4.7 旅行商(TSP)问题

修改圈近似算法:
首先求一个Hamilton圈C,然后适当修改C已得到具有最小圈的另一个Hamilton圈。修改的方法交做改良圈算法。
此算法无法得到精确解。

4.8 计划评审方法和关键路线法

4.9 钢管订购和运输

由于两部分涉及程序均为lingo程序,由于本人知识有限,便不对lingo程序相关内容展开详解,有需要的可以看原书。无资料可以私我提供pdf文件。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值