图的应用
4.1 拓扑排序
拓扑排序针对有向无环图的顶点进行线性排列的算法,使得对于任何来自顶点A指向顶点B的边,A都在序列中出现在B之前。这样的排序存在于有向无环图中,而对于非有向无环图则不存在拓扑排序。
拓扑排序也可以用来检测图中有无成环
4.2 最小生成树
生成树是连通图的极小连通子图,多一条边就会成环,少一条边即无法构成连通图
生成树的代价:
G=(V,E)是一个无向连通图,代价是生成树上各边的边权和
生成树代价=9+49+11+1+21=91
最小生成树即是代价最小的生成树
上图中代价min=1+7+10+7+5=30
最小生成树解析
4.2.1 Kruskal(加边)
将边从小到大排序
利用并查集 合并边
如果该边的加入不会成换,则加入,否则不加入
时间复杂度分析:边排序时间+并查集时间
蓝桥889
# n 个交叉路口 -- n个点 m条边 无向图`
`# 最小生成树`
`# 1≤n≤300,1≤m≤1e5 边>>点 稠密图 最好用Prmie`
`# Kruskal版`
`n,m =` `map(int,input().split())`
`# 存图`
`e =` `[]`
`# m条边 连边`
`for i in` `range(m):`
` u,v,c =` `map(int,input().split())`
` e.append((c,u,v))` `# 把分值c放第一位,方便后续排序`
`# 边排序`
`e.sort()`
`# 把所有的交叉路口直接或间接的连通起来 -- 并查集`
`p =` `list(range(n+1))`
`# 递归找根`
`def` `find_root(x):`
`if x != p[x]:`
` p[x]` `= find_root(p[x])`
`return p[x]`
`# 从小到大遍历所有边,进行合并 找最小的生成树`
`# sum记录边权和,max_val记录最大的边权`
`sum, max_val =` `0,0`
`for w,u,v in e:`
` rootu = find_root(u)`
` rootv = find_root(v)`
`if rootv != rootu:`
` p[rootv]` `= rootu`
`# 合并次数+1 修路的条数+1`
`sum` `+=` `1`
` max_val =` `max(max_val,w)`
`print(sum,max_val)`
`
4.2.2 Prime(加点)
算法流程:d[i]表示点i和集合的距离
选择一个初始点(随意)u加入集合S,d[u]=0
利用点u更新其它的点(加入离当前集合最近的点)
在d中选择最小未加入集合的点作为新一轮的u
重复上述至所有点加入集合(过程中有两个分区,一个是加入集合的点,另一个是还未加入的点)
# 蓝桥889`
`# n 个交叉路口 -- n个点 m条边 无向图`
`# 最小生成树`
`# 1≤n≤300,1≤m≤1e5 边>>点 稠密图 最好用Prmie`
`# Prime版`
`import math`
`n,m = map(int,input().split())`
`INF = math.inf`
`# 存图`
`mapp = [[INF]*(n+1) for _ in range(n+1)]`
`for _ in range(m):`
` u,v,w = map(int,input().split())`
` mapp[u][v] = mapp[v][u] = min(mapp[u][v],w)`
`# 定义集合d`
`d = [INF] * (n+1)`
`max_val = 0`
`# 初始化d[1]=0`
`u = 1`
`# 跑Prime加点`
`for i in range(n-1): # 除去第一个点,还有n-1个点`
` d[u] = 0`
` # 初始化下一个点,下一条边`
` next_u = 0`
` next_val = INF`
` for v in range(1,n+1):`
` if d[v] == 0: continue # 该点不与当前集合d连通`
` d[v] = min(d[v], mapp[u][v]) # mapp[u][v]点u到v的边权`
` # 第i次加点时找到离当前集合更近的点`
` if d[v] < next_val:`
` next_val = d[v]`
` next_u = v`
` # 基于上一轮扩大下一轮集合,更新d`
` u = next_u`
` max_val = max(next_val, max_val)`
`print(n-1, max_val)`
`
4.3 最短路问题
最短路径是两个顶点之间经历的边上边权之和最小的可达路径
4.3.1 Floyed
用于处理多源最短路,从多个点出发到一个点的最短路,可以存在负权边。
蓝桥8336
# n点m边`
`# floyd 利用动态规划 三层循环`
`# 定义dp[k][i][j]表示点i到点j的路径(除去起点和终点)中编号最大不超过k的情况下,i到j的最短距离`
`# 当加入 第k个点 作为i到j的 中间点`
`# dp[k][i][j] = mian(dp[k-1][i][j], dp[k-1][i][k]+dp[k-1][k][j])`
`import math`
`n,m =` `map(int,input().split())`
`# 城市i的商品产量`
`a =` `[0]` `*` `(n+1)`
`# 城市i的商品生产成本`
`p =` `[0]` `*` `(n+1)`
`# 城市i的商品售卖单价`
`s =` `[0]` `*` `(n+1)`
`INF = math.inf`
`# 一件商品从城市i运往城市j的利润:gi,j=sj-pi-fij`
`# 其中fi,j是路径费用`
`f =` `[[INF]` `*(n+1)` `for _ in` `range(n+1)]` `# fij为INF表示不通路`
`g =` `[[0]` `*(n+1)` `for _ in` `range(n+1)]`
`# 输入a,p,s`
`for i in` `range(1,n+1):`
` a[i],p[i],s[i]` `=` `map(int,input().split())`
`# 输入图`
`for i in` `range(1,m+1):`
` u,v,w =` `map(int,input().split())`
` f[u][v]` `= f[v][u]` `=` `min(f[u][v],w)`
`# 对角线费用为0 n个点 `
`for i in` `range(1,n+1):`
` f[i][i]` `=` `0`
`# floyd 动态规划 三层循环 跑一遍多源全图最短路填f路费表`
`for k in` `range(1,n+1):`
`for i in` `range(1,n+1):`
`for j in` `range(1,n+1):`
` f[i][j]` `=` `min(f[i][j], f[i][k]` `+ f[k][j])`
`# 填完路费 现在算利润 填g 把i城市生产的产品送到城市j卖`
`for i in` `range(1,n+1):`
`for j in` `range(1,n+1):`
` g[i][j]` `= s[j]` `- p[i]` `- f[i][j]`
`# 求全图的最大利润`
`max_g =` `0`
`for i in` `range(1,n+1):`
`# 在第二层循环中作一个判断是否有交易的依据`
` max_s =` `-1`
`for j in` `range(1,n+1):`
` max_s =` `max(max_s,g[i][j])`
` max_g +=` `max(0, max_s)` `* a[i]` `# 乘上产品数量`
`print(max_g)`
`
4.3.2 Dijkstra
用于处理单源最短路,从一个点出发到一个点的最短路,不可以存在负权边。
# n点m边 有向图`
`# 从皇宫到每个建筑的最短路径是多少 -- 单源最短路`
`from queue import PriorityQueue`
`import math`
`INF = math.inf`
`# dijkstra ,s:起点`
`def` `dijkstra(s):`
`# 求从起点s出发到各个点i的最短路径`
`# d[i]表示从起点s出发到点i的最短的最短路径`
` d =` `[INF]*(n+1)`
`# vis[i]表示第i个点是否出队列`
` vis =` `[0]*(n+1)`
`# 创建优先队列`
` q = PriorityQueue()`
`# 起点初始化距离0`
` d[s]` `=` `0`
`# 起点入队列`
` q.put((d[s],s))`
`# 当队列非空`
`while q.queue:`
` dis,u = q.get()`
`# 每个点只有第一次出队列有效`
`if vis[u]:continue`
` vis[u]` `=` `1`
`# 松弛 找离当前点在连通状态下的最近点`
`for v,w in G[u]:`
`if d[v]` `> d[u]` `+ w:`
` d[v]` `= d[u]` `+ w`
` q.put((d[v],v))`
`# 处理完成d数组后按题目要求不通的距离为视为-1 `
`for i in` `range(n+1):`
`if d[i]` `== INF:`
` d[i]` `=` `-1`
`print(*d[1:],sep=' ')`
`n,m =` `map(int,input().split())`
`# 存图`
`G =` `[[]` `for i in` `range(n+1)]`
`for _ in` `range(m):`
` u,v,w =` `map(int,input().split())`
` G[u].append([v,w])`
`dijkstra(1)`
`