您好,欢迎访问三七文档
图论相关算法图的组织形式点、边(有向、无向)、权(容量、费用)矩阵、邻接表广度优先搜索通常用队列(先进先出,FIFO)实现Q={起点s};标记s为己访问;while(Q非空){取Q队首元素u;u出队;所有与u相邻且未被访问的点进入队列;标记u为已访问;}广度优先搜索由BFS得到的路径是原图中从S到T的边数最少的路径广度优先搜索树不唯一广度优先搜索双向广度优先搜索适用于已知出发点和结束点的BFS减少不必要的状态,搜索加速分析:搜索分支因子为r(每次可扩展状态数),需要k层,总状态数为rk,双向,前后各rk/2,总状态数2×rk/2哈希判重广度优先搜索无向图的连通分量无向图的连通分量是指,在连通分量里面的任意两个点之间都有路。易知从某个点v开始进行一次BFS,遍历到的所有点和v就在同一个连通分量内。广度优先搜索例题:无向单位边权值图,300000个点,编号1~n,列出所有中心点的编号中心点:到图中其他所有点的最长距离最小的点广度优先搜索解法:广搜,遍历原图,生成BFS树求树的直径,即树的最长路直径的中间节点(1个或者2个)为中心点广度优先搜索树的直径求法:选任意点开始BFS选BFS中深度最大的一个点为直径的一个端点从该端点出发再BFS最深的节点为另一端点,且深度为直径长度深度优先搜索相关概念递归实现结点颜色:白色(开始),灰色(发现),黑色(结束)一个结点总是从白色变为灰色,再变为黑色当所有点都变为黑色时,遍历结束时间戳(timestamp):d[u]表示一个结点开始被访问的时间,f[u]表示一个结点结束访问的时间深度优先搜索inttimestamp=0;dfs(intp){timestamp=timestamp+1;col[p]=GREY;d[p]=timestamp;for(每个与p相邻的点i)if(col[i]==WHITE)dfs(i);timestamp=timestamp+1;f[p]=timestamp;col[p]=BLACK;}深度优先搜索每个顶点的d[u]f[u]DFS中,v是u的子孙d[u]d[v]f[v]f[u]在搜索中发现u时可以从u出发沿一条完全由白色顶点组成的路径到达vm条边的连通图,DFS中顺序标号每条边为1~m,则任意与顶点u关联的所有边(边数=2)的标号的GCD为1深度优先搜索割点与割边如果从图中删去某点和与该点相关联的边后,图不再连通,那么这个点叫做割点(cutpoint)。如果从图中删去某条边后,图不再连通,那么这条边叫做割边或桥(bridge)。深度优先搜索思路dep[u]记录节点u在DFS树中的深度low[u]记录节点u的子孙所能达到的最浅深度u为割点u为根,且有大于1个儿子u不为根,且u的某个儿子v有low[v]=dep[u](u,v)为割边点u的某个儿子v,有low[v]dep[u]深度优先搜索dfs(intk,intfather,intdepth){col[k]=GREY;dep[k]=depth;tot=0;for(每个与k相邻的点i){if(i!=father&&col[i]==GREY)low[k]=min(low[k],dep[i]);if(col[i]==WHITE){dfs(i,k,depth+1);tot=tot+1;low[k]=min(low[k],low[i]);if((k为根&&tot1)||(k不为根&&low[i]=dep[k]))k为割点;if(low[i]dep[k])then(k,i)为割边;}}col[k]=BLACK;}深度优先搜索例题:某公司拥有N台计算机,并将他们组成局域网,现在要求寻找该局域网中的关键点(即割点),并求出一旦处于该关键点的计算机崩溃,原局域网将会分成多少个子网深度优先搜索SampleInput125431323435012233445510122334466325510深度优先搜索SampleOutputNetwork#1:SPFnode3leaves2subnetsNetwork#2:NoSPFnodesNetwork#3:SPFnode2leaves2subnetsSPFnode3leaves2subnets深度优先搜索解法:求割点,计算删除该点使连通分量增加数对于DFS森林,对每个割点记录一个cnt值,若有一个儿子v使得low[v]=dep[u]则使cnt[u]++,则有如下性质:对每个非根割点,删除该节点会增加cnt[u]个连通分量对每个根割点,删除该点会使图的连通分量增加其儿子数-1个拓扑排序图的结点存在一个拓扑序:如果图中有边(u,v),则u必定排在v的前面拓扑排序在有向无环图(DAG)上进行拓扑序列不一定唯一拓扑排序利用DFS,当节点u已经扩展完成,将标记颜色置为黑时,将u加入已排序列的顶部,搜索完成时,节点序列为拓扑序经过拓扑排序的顶点以与其完成时刻相反的顺序出现拓扑排序算法(1)计算每个点的入度,入度为0的点加入队列Q(2)从Q中取出一个点p,输出(3)所有与p相邻的点的入度减1。如果新得到了入度为0的点,则加入队列Q。(4)转步骤(2),直到所有点都输出完毕如果在执行过程中发现找不到入度为0的点,说明图中存在环拓扑排序13245678(不唯一)如何生成最小字典序的拓扑序列?可以使用最小堆维护当前入度为0的节点21348756强连通分量有向图的强连通分量(SCC)指:对于强连通分量里面的任意两个节点u和v,都存在u到v和v到u的路强连通分量思路:对原图G进行DFS,记录各点的结束时间,把原图G的所有边反向为GT,在GT以各点结束时间递减的顺序DFS,则每棵树都是一个强连通分量即第一遍DFS生成拓扑序,以拓扑序做第二次DFS强连通分量缩点操作生成一个新的有向图Gscc,将每个强连通分量作为新图的一个点,原图连通分量内部的边删除,连通分量之间的边保留新图必定有拓扑序,即不会出现有向环(DAG)Gscc称为原图的核心DAG强连通分量强连通分量例题:队长要向所有人通知某件事情,因为人数众多,他想出了一个省力的方法:他只通知队中的某些人,然后让这些人去通知所有他们认识的人,这些新被通知的人又去通知更多的人……直到最后队中的所有人都被通知到。给定队长通知每个人所需的花费,现在他想求出一种方案,使得花费最少,并且保证最终所有人都能被通知到。强连通分量SampleInput4330201040122123SampleOutput60强连通分量解题:同一个强连通分量里,只要有一人被通知即可缩点后得到的DAG中,如果一个点被通知,则它的所有后继结点都会被通知。故只需通知入度为0的点在入度为0的每个点所表示的连通分量中,通知花费最少的那个人,即为最优解强连通分量例题:给一个有向图G,添加最少的边数使得G中各点能两两互通(即每对点a和b,a能到b,b也能到a),求最少的边数强连通分量解题:对原图求强连通分量,形成新的DAG图Gscc对Gscc计算入度为0的点的个数为x对Gscc计算出度为0的点的个数为y答案为max(x,y),即从出度为0的点向入度为0的点连max(x,y)条边欧拉路欧拉路:经过图中每条边恰好一次的路欧拉回路:起点和终点相同的欧拉路欧拉路无向图存在欧拉路的条件如果图连通,且所有的点都是偶数度,则有欧拉回路。如果图连通,且恰有两点是奇数度,则有欧拉路。且欧拉路的起止点为这两个奇数度点。对重边、自环的情况仍适用。欧拉路有向图存在欧拉路的条件如果图连通,且每个点的入度等于出度,则存在欧拉回路。如果图连通,且恰有一点u的出度比入度大1,另有一点v的出度比入度小1,其余的出度等于出度,则存在欧拉路,起点为u,终点为v。对重边、自环的情况仍适用。欧拉路套圈算法voidEuler(intstart){for(inti=1;i=E;i++){if(!visit[i]&&from==start){visit[i]=1;Euler(to);path[++top]=i;}}}倒序path[]为欧拉路欧拉路例题:给定一组单词,问这些单词能否连成一串,使得前面一个单词的最后一个字母和后面一个单词的第一个字母相同。欧拉路SampleInput6alohaarachniddoggopherrattiger3oakmapleelmSampleOutputaloha.arachnid.dog.gopher.rat.tiger***欧拉路解法:先判断连通性每个单词相当于在首字母和尾字母之间连一条边得到一个最多26个点的有向图求欧拉路汉密尔顿路汉密尔顿路:经过图中每个点恰好一次的路汉密尔顿回路:起点和终点相同的汉密尔顿路常见方法:状态压缩DP汉密尔顿路对于点数较少的情况,可以用状态压缩的DP来做。比如用opt[mask][k]表示已经访问过mask表示的结点集合,并且最后位于点k的最优解。状态转移方程为:opt[mask][k]=min(opt[mask-{k}][i]+cost[i][k])i是所有与k相邻的点,mask-{k}指从mask除去k最短路径单源最短路径DijkstraBellman-FordSPFA任意两点间Floyd最短路径复杂度:Dijkstra:O(V2)或O(E+VlgV)Bellman-Ford:O(EV)Floyd:O(V3)SPFA:期望复杂度O(E)最短路径算法选择:编程复杂度:Bellman-FordFloydDijkstra时间复杂度:DijkstraBellman-Ford使用范围:有负权边需要使用Bellman-Ford如果是稀疏图,即使是求任意两点间最短路径,也最好选用堆优化的Dijkstra最短路径单源最短路径的核心松弛操作If(d[j]d[i]+cost[i][j])d[j]=d[i]+cost[i][j];最短路径Dijkstra最短路径Bellman-Ford主体思想:对图中每条边做|V|-1次松弛操作,即可确定最短路径,当有负权值时,再对所有边做一次松弛操作,若dis值仍有变化则表明存在负环最短路径boolBellman(intn,intm){/*bellman,returnfalsewhenthereisanagetivecircle*/intu,v,w,i,j;boolflag;for(i=1;i=n;i++){flag=0;for(j=0;jm;j++){u=edge[j].from,v=edge[j].to,w=edge[j].value;if(dis[v]dis[u]+w){dis[v]=dis[u]+w;flag=1;}}if(!flag)break;if(i==n&&flag)returnfalse;}returntrue;}最短路径SPFA作为一种动态逼近的方法,是一种迭代模式,不仅仅可以用在最短路中,可以解决带环的DP问题最短路径Q={s};d[s]=0;while(Q非空){取队首元素u;u出队;for(与u相邻的点v){if(d[v]d[u]+cost[u][v]){d[v]=d[u]+cost[u][v];if(v不在队中)thenv入队;}//若某个点入队次数达到|v|说明有负环}}可以用循环队列实现,队列中元素个数不超过|V|最短路径例题:最小平均环路图的每条边有两个权值a和b,在图上找一个环,使得对于环上的所有边(∑a/∑b)最小最短路径解法:二分答案设答案为k,使各边的边权值为k×b[i]–a[i],在新图中用Bellman-Ford或者SPFA判断是否含有负环若有负环,则k×b[i]–a[i]
本文标题:图论相关算法
链接地址:https://www.777doc.com/doc-5171806 .html