1、与图论相关的算法,图的基本概念,G=(V,E) V表示顶点集 E表示边集 无向图和有向图 如果边都是双向的,则这个图叫做无向图。 如果边都是单向的,则这个图叫做有向图。 简单图 环:端点重合为一点的边。 重边:两条边连接同一对顶点。 简单图:没有环和重边的图。,图的基本概念,完全图:边数达到最大值的无向简单图。 顶点的度 无向图中,一个顶点相连的边数称为该顶点的度。 有向图中,从一个顶点出发的边数称为该顶点得出度;到达该顶点的边数称为它的入度。 顶点的最大度数称为图的度数。 路和回路 一个连接两个顶点的,顶点与边交替的序列称为路。 除了起始与终止顶点,其他顶点都不相同,这样的路被称为简单路径。
2、 起始与终止顶点相同的简单路径称为圈。,图的基本概念,子图 连通性 无向图中,如果两个顶点之间存在一条路经,就称这两个顶点是连通的。 有向图中,如果两个顶点之间相互都存在一条路,则称它们是强连通的。 如果一个图的任意两个顶点都是连通的,就称这个图是连通的。,图的基本概念,二分图 二分图的顶点可以分成两个非空集合X和Y,使得每一条边都有一个顶点在集合X里面,另一个顶点在集合Y里面。 完全二分图:特殊的二分图,集合X里面的每一个顶点与集合Y里面的每一个顶点相连。,例题:空中都市,有一些小岛,要架设桥梁。如果A与B之间有桥,B与C之间有桥,则A与C之间不能有桥。要计算最多可以架设几座桥。 输入是一个
3、正整数n(0=n=1000),表示小岛的数量。,图的存储方式(1),相邻矩阵表示法相邻矩阵表示的特点: 计算结点的度很方便 可通过矩阵的乘法来判断连通性 存储无向图时,可以节省将近一半的空间,图的存储方式(2),邻接表表示法 常用于稀疏矩阵 结点信息:出度、访问标志和边表首指针 边信息:另一结点编号、边的权值和下一条边的指针,图的存储方式(2),邻接表定义: constmaxn:=顶点数; typearcptr=arcnode;arcnode=recordv:integer;weight:real;nextarc:arcptr;end;vertexnode=recordm:integer;vi
4、sited:boolean;firstarc:arcptr;end; varmap:array1maxn of vertexnode;,图的遍历,深度优先搜索 广度优先搜索 计算连通分支数,深度优先搜索,procedure dfs(i:integer); var q:arcptr; begin访问并处理顶点i;mapi.visited=true;q:=mapi.firstarc;while qnil do beginif mapq.v.visited=false then dfs(q.v);q:=q.nextarc;end; end;,广度优先搜索,procedure bfs(i:intege
5、r); var p:arcptr;closed,open:integer;q:array1maxn of integer; begin访问并处理顶点i;mapi.visited:=true;顶点i进入队列q;closed:=0; open:=1;repeatinc(closed);v:=qclosed;p:=mapv.firstarc;while pnil do beginif mapp.v.visited=false then begin访问并处理顶点q.v;mapq.v.visited=true;inc(open);qopen:=q.v;end;q:=q.nextarc;end;until
6、 closed=open; end;,计算连通分支数,count:=0; for i:=1 to n do mapi.visited:=false; for i:=1 to n do if mapi.visited=false do begininc(count); dfs(i);end;,最小生成树,生成树:树结构的子图。 最小生成树:权最小的生成树。 Prim算法 Krusal算法,Prim算法基本思想,任取一个顶点加入生成树; 在那些一个端点在生成树里,另一个端点不在生成树里的边中,取权最小的边,将它和另一个端点加进生成树; 重复上一步骤,直到所有的顶点都进入了生成树为止。,Krusal
7、算法基本思想,对所有边从小到大排序; 依次试探将边和它的端点加入生成树,如果加入此边后不产生圈,则将边和它的端点加入生成树;否则,将它删去; 直到生成树中有了n-1条边,即告终止。,例题:最小公路造价,计算机程序设计P144 使用Krusal算法 对于这道题,使用任何一种方法都差不多。但是在有些情况下,选择合适的方法能降低编程的复杂度或程序的时间复杂度,例如2002年ACM/ICPC总决赛里面的一道关于在岛屿间建立通讯网络的题目。,最短路问题,单源点最短路 单目标点最短路 单对结点间最短路 每对结点间最短路等,单源点最短路的Dijkstra算法,基本思想 把图中的所有顶点分为两组: 第一组包括
8、已经确定最短路径的顶点; 第二组包括尚未确定最短路径的顶点; 初始时,v0进入第一组,距离值为0;第二组包含其他的所有顶点,距离值是,单源点最短路的Dijkstra算法,基本思想 在第二组中选取距离值最小的顶点vm,将其加入第一组; 对第二组所有顶点的距离值进行修正:,例题:Car的旅行线路,计算机程序设计P149 书上的算法与Dijkstra算法一样吗? 不一样,书上的程序复杂度高,例题:最优乘车,H城是一个旅游胜地,为方便游客,巴士公司在各个旅游景点及宾馆,饭店等地都设置了巴士站并开通了一些环线巴士线路。一名旅客最近到H城旅游,他很想从他所在的饭店去S公园游玩,他可能要先乘某一路巴士坐几站
9、,再下来换乘同一站台的另一路巴士, 这样换乘几次后到达S公园。用整数1,2,N 给H城的所有的巴士站编号,约定这名旅客所在饭店的巴士站编号为1,S公园巴士站的编号为N。写一个程序,寻找一个最优乘车方案,使得换车的次数最少。 输入文件是INPUT.TXT。文件的第一行有两个数字M和N(1=M=100,1N=500),表示开通了M条环线巴士线路,总共有N个车站。从第二行到第M+1行依次给出了第1条到第M条巴士线路的信息。其中第i+1行给出的是第i条巴士线路的信息,从左至右按运行顺序依次给出了该线路上的所有站号相邻两个站号之间用一个空格隔开。 输出文件是OUTPUT.TXT,文件只有一行。如果无法乘
10、巴士从饭店到达S公园,则输出“N0“,否则输出你的程序所找到的最少换车次数,换车次数为0表示不需换车即可到达。,例题,Domino效应 网络提速 最佳路线,Bellman_Ford算法,一个有负权的有向图G的源点为S,Ford算法返回一个布尔值。当图中存在负权回路时,返回false;若不存在负权回路,则返回true,并产生单源最短路径。,Bellman_Ford算法的基本思想,对每一点v,设置变量dv,描述从s到v的最短路径的权的上限。设置变量pv,保存当前最短路径中v的前驱顶点。 初始化 dv=infinity; ds=0; pv=infinity; 对每一条边,做|V|-1次松弛操作。 判
11、断是否有负权回路。,松弛操作,对于边(u,v),如果dvdu+wu,v,作如下修改:dv=du+wu,v,pv=u;否则,dv保持不变。,判断是否有负权回路,再做一次松弛操作,也就是说如果存在一条边(u,v),使得dvdu+wu,v,那么图中一定存在负权回路,返回false;否则,说明图中没有负权回路,返回true,并且根据pv得到s到每一点的最短路径。,Ford算法松弛操作,例题,蛀洞,Floyd算法,求每一对顶点间的最短路问题,可以使用n次Dijkstra算法,复杂度为O(n3)。 Floyd算法用于求每一对顶点间的最短路问题,复杂度同样为O(n3)。,Floyd算法的基本思想,递推产生矩
12、阵序列adj(0), adj(1), , adj(n)。 其中adj(0)i,j=adji,j adj(k)i,j的值表示从vi到vj,中间结点编号不超过k的最短路径长度。 adj(n)i,j的值就是从vi到vj的最短路径长度。,递推过程,如果adj(k-1)i,jadj(k-1)i,k+adj(k-1)k,j ,那么就令adj(k)i,j= adj(k-1)i,k+adj(k-1)k,j。,计算图的传递闭包,如果将每一对顶点间的最短路问题进行简化,只要判断每一对点间是否存在一条路径,这样的问题称作图的传递闭包。 解决这个问题的一种方法是利用邻接矩阵的乘法。 另一种方法是用Floyd算法。,用
13、Floyd算法求图的传递闭包,基本思想 给每一条边赋权1 使用Floyd算法 最后,如果adji,jn,则表明vi与vj之间有路,否则vi与vj之间没有路。 但是这么做下来,不但知道了两点之间有没有路径,还知道了它们之间的经过顶点数最少的路径长度。是否有更好的算法呢?,求传递闭包,永布尔类型的数组ti,j来记录vi到vj是否有路径。 递推计算t(0)i,j, t(1)i,j, , t(n)i,j。 t(k)i,j表示从vi到vj是否存在中间顶点的编号都不超过k的路径。 递推公式: t(k)i,j= t(k-1)i,j or (t(k-1)i,k and t(k-1)k,j),Johnson算法
14、,Johnson算法常用于求顶点个数较多的稀疏图的每对点间最短路问题。 感兴趣的同学请参阅国际信息学奥林匹克竞赛指导实用算法的分析与程序设计,拓扑排序,给出有向图G=(V,E),若顶点的一个序列满足条件:如果(u,v)属于图E,那么u排在v的前面 ,那么该序列就被称为拓扑序列。 一个有向图的拓扑序列不唯一,而且可能不存在拓扑序列。 无圈的有向图都可以构造出拓扑序列,这个过程称为拓扑排序。,拓扑排序,基本思想 从图中选择一个入度为0的顶点加入拓扑序列; 从图中删除该顶点和它的所有出边; 重复上面两个步骤,直到所有的顶点都进入了拓扑序列为止。,例题:士兵排队问题(P154),有个士兵(126),编
15、号依次为,,队列训练时,指挥官要把一些士兵从高到矮一次排成一行,但现在指挥官不能直接获得每个人的身高信息,只能获得“P1比P2高”这样的比较结果(P1、P2,记为 P1P2),如”表示比高。请编一程序,根据所得到的比较结果求出一种符合条件的排队方案。(注:比较结果中没有涉及的士兵不参加排队) 输入要求:每个比较结果在文本文件中占一行。 输出要求:若输入数据无解,打印“No Answer!”信息,否则从高到矮一次输出每一个士兵的编号,中间无分割符。,求有向图的强连通分支,基本思想 对图G进行深度优先搜索,将顶点按照完成时刻递减排序。 从上面的顶点序列,逐个取出未访问的顶点,对图G的转置进行深度优
16、先搜索,访问到的顶点在同一个强连通分支里。,数据结构,n,flag:integer; /n顶点数 flag转置标记 g:array1maxn,1maxn of integer; c,a1,a2:array1maxn of integer; /c顶点颜色表 a1,a2完成时刻排序表 color,m:integer;,procedure dfs;,a2:=a1; fillchar(c,sizeof(c),0); color:=0; m:=n; for u:=1 to n doif ca2u=0 then begininc(color);dfs_visit(a2u);if flag=1 then w
17、riteln;end;,procedure dfs_visit(u:integer);,cu:=-color; for v:=1 to n doif (gu,v0) and (cv=0) then dfs_visit(v); cu:=color; if flag=1 then write(u, ) else begina1m:=u;dec(m);end;,主程序,flag:=0; input; for i:=1 to n a1i:=i; dfs; gt; /转置 flag:=1; dfs;,求无向图的割点和桥,在深度优先搜索过程中,保存前驱表、出度表、标号函数表和发现时刻表。 请参阅国际信息学
18、奥林匹克竞赛指导实用算法的分析与程序设计。,问题:控股问题,如果至少满足下述条件之一,则说A公司控制B公司: (1)AB; (2)A持有B的50以上的股份; (3)控制着K(K1)个公司,被控制的公司名为:c(1),c(2),,c(i),c(K) (1iK)这些被A控制的公司c(i)还持有B公司的x(i)%的股份,且:x(1)+x(2)+x(i)x(k)50%(1iK) 给定三元组(i,j,p),其含义是:公司i持有公司j的股份的P,求出二元组(h,s),它表示公司H控制公司S,公司的总数100。 编写一个程序:1.从ASCII输入文件COMPANY.DAT中读入数据,它是一个三元组表,共中每个三元组代表一个数据,并且i,j,p都是正整数。不同的每组数据间(指三元组之间)用空行隔开。2.找到所有的二元组(h,s):公司h控制着公司s。3.向名为COMPANY.SOL的ASCII文件写输出结果,其中含所有的二元组(h,s),请注意,h不能和s相同。并且,按h的升序输出结果。各个不同结果数据组之间用空行隔开。,