Topics on Graph Theory (various algorithms and Blue Bridge Cup real test questions)

1. Introduction to Graph Theory

1.1 Side deposit method

1.1.1 Array edge storage

 1.1.2 Adjacency matrix edge storage

 1.1.3 Adjacent table storage edge

 1.2 Graph traversal and connectivity

Traverse each graph through DFS and BFS

For non-connected graphs, the loop operates on each point dfs

Connectivity can also be judged by union search

1.2.1 Example of global warming

import sys
sys.setrecursionlimit(60000)  # 设置最大递归深度,默认递归深度有点小,不设置递归会出问题
 
def dfs(x,y):
  d=[(-1,0),(0,1),(1,0),(0,-1)]  # 左 上 右 下
  global flag
  vis[x][y] =1
  if mp[x][y+1]=='#' and mp[x][y-1] =='#' and mp[x+1][y]=='#' and mp[x-1][y]=='#':
    # 说明点(x,y)四周都是陆地
    flag = 1  # 高地标记,不会被淹没
  for i in range(4):
    nx=x+d[i][0]
    ny=y+d[i][1]
    if vis[nx][ny] ==0 and mp[nx][ny] =='#':
      # 如果当前没有遍历点(nx,ny)同时地图上面该点不是陆地
      dfs(nx,ny)
 
n=int(input())
mp = []   # 记录地图
for i in range(n):
  mp.append(list(input()))
vis =[]   # 判断是否走过
for i in range(n):
  vis.append([0]*n)
 
ans =0
for i in range(n):  # 遍历每一点
  for j in range(n):
    if vis[i][j] ==0 and mp[i][j] =='#':  # 相当于找连通分量
      flag = 0
      dfs(i,j)
      if flag==0:
        ans+=1
 
print(ans)
    

1.3 Euler Road and Euler Circuit

 Hamiltonian circuit: every point in the graph passes through and only once

1.3.1 Euler Road and Euler Circuit Judgment

        Undirected graph

        If the points in the graph are all even points, there is an Euler circuit; any point can be used as the starting point and the ending point.

        An Euler road exists if there are only 2 singularities, one of which is the starting point and the other is the ending point. It is impossible to have an undirected graph with an odd number of singularities.

        directed graph

        The out-degree of a point is recorded as 1, and the in-degree is recorded as -1. The sum of all out-degrees and in-degrees on this point is its degree.
        A directed graph has an Euler circuit if and only if all vertices in the graph have degree zero.
        If there is only one point of degree 1, one point of degree -1, and all other points of degree 0, then there is an Euler path where the point of degree 1 is the starting point and the point of degree -1 is the end point.

1.3.2 Example of Euler Road King

## 不全,没看懂
import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)

def dfs(u):
    i=d[u]   # 从点u的第一条边i=0开始
    while(i<len(G[u])):
        d[u]=i+1  # 后面继续走u的下一条边
        dfs(G[u][i]) # 继续走这条边的邻居点
        i=dp[u]   # 第i条边走过了,不再重复走
    rec.append(u)

M=100100
n,m = map(int,input().split())  # n个点,m条边
du=[[0 for _ in range(2)] for _ in range(M)]  # 记录入度,出度
G=[[] for _ in range(n+1)]  # 临接表存图
d=[0 for _ in range(M)]  # d[u]=i : 当前走u的第i个边
rec=[]   #记录欧拉路
    

for i in range(m):
    u,v =map(int,input().split())
    G[u].append(v)
    du[u][1]+=1   #出度
    du[v][0]+=1   #入度
for i in range(1,n+1):
    G[i].sort()   # 对邻居点排序,字典序
    S=1

2. Floyd algorithm

2.1 Introduction to Floyd

 Algorithm comparison

 2.2 Algorithm template

import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)

def floyd():
    for k in range(1,n+1):
        for i in range(1,n+1):
            for j in range(1,n+1):
                if dp[i][k]+dp[k][j] <dp[i][j]:
                    dp[i][j]=dp[i][k]+dp[k][j] 

 2.3 Algorithm Summary

 2.4 Algorithm Examples

2.4.1 Blue Bridge Park

import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)



def floyd():
  for i in range(1,n+1):
    for j in range(1,n+1):
      for k in range(1,n+1):
        dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])
 
n,m,q = map(int,input().split())
inf=2**120  #自定义无穷大
dp=[[inf]*(n+1) for i in range(n+1)]  # 初始为无穷大
choice=[]
for i in range(m):
  u,v,w=map(int,input().split())   # 无向图,临接矩阵存边
  dp[u][v]=w
  dp[v][u]=w 
for i in range(q):   # 读 起点和终点
  s,d = map(int,input().split()) 
  choice.append((s,d))
floyd()
for s,d in choice:
  if dp[s][d]!=inf:
    print(dp[s][d])
    continue
  print(-1)
   

2.4.2 Path

 standard floyd algorithm

import sys
import collections
import itertools
import heapq
import math
sys.setrecursionlimit(300000)


# 标准的floyd


def lcm(x,y):   # 求最下公倍数
    return x//math.gcd(x,y)*y

def floyd():
    for k in range(1,2022):
        for i in range(1,2022):
            for j in range(1,2022):
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])

        
dp=[[int(0x3f3f3f3f3f3f3f) for _ in range(2022)] for _ in range(2022)]
for i in range(1,2022):
    for j in range(1,2022):
        if abs(i-j)<=21:  # 题意中的如果两个结点的差的绝对值不大于21
            dp[i][j]=lcm(i,j)

print(dp[1][2021]

 Simplified version of Floyd's algorithm

import sys
import collections
import itertools
import heapq
sys.setrecursionlimit(300000)


import math
def lcm(i,j):
  return i//math.gcd(i,j)*j
 
dp=[[2**50]*2022 for i in range(2022)]
# 创建图
for i in range(1,2022):
  for j in range(i,2022):  
    if abs(i-j)<=21:
      dp[i][j]=lcm(i,j)
 
# 找最短路径
for k in range(1,2022):
  for i in range(1,2):
    for j in range(1,2022):
      dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])
print(dp[1][2021])  # 1026837
 

Bellman-Ford Algorithm

import sys
import collections
import itertools
import heapq
import math
sys.setrecursionlimit(300000)


# Bellman_Ford算法


def lcm(x,y):   # 求最下公倍数
    return x//math.gcd(x,y)*y
        
dp=[int(0x3f3f3f3f3f3f3f) for _ in range(2022)]
for i in range(1,2022):
    for j in range(i+1,i+22): # 题意中的如果两个结点的差的绝对值不大于21
        if j>2021: break
            dp[j]=min(dp[j],dp[i]+lcm(i,j))  # 更新最短路

print(dp[2021])

3. Dijstra algorithm

3.1 Algorithm Introduction

 

 3.2 Algorithm example

 

 

 3.3 Example template

3.3.1 Blue Bridge Kingdom

import sys
import collections
import itertools
import heapq   # 默认小顶堆
import math
sys.setrecursionlimit(300000)


def dij(s):
    vis=[0 for i in range(n+1)]  # 标志是否访问过
    hp=[]  # 堆
    dis[s]=0  # 自身到自身的距离为0
    heapq.heappush(hp,(0,s))  # 列表堆化同时入堆
    while hp:
        u=heapq.heappop(hp)[1]  # 出堆,出的是结点
        if vis[u]: # 判断是否处理过
            continue
        vis[u]=1
        for i in G[u]:
            v,w=i[0],i[1]
            if vis[v]:
                continue
            if dis[v]>dis[u]+w:
                dis[v]=dis[u]+w
                heapq.heappush(hp,(dis[v],v))
    

n,m=map(int,input().split())
s=1
G=[[]for i in range(n+1)]  # 临接表存图
inf=2**64
dis=[inf]*(n+1)   # 从1到其他点的距离
for i in range(m):  # 邻接表存m条边
    u,v,w=map(int,input().split())
    G[u].append((v,w))
dij(s)   # 以s为起点到其他点的最短路径
for i in range(1,n+1):
    if dis[i]>=inf :
        print("-1",end=' ')
    else:
        print(dis[i],end=" ")
    

3.3.2 Dijstra template in matrix-matrix form

 

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
 
 
 
def dij():
   dist[1]=0  #很重要
   for _ in range(n-1): # 还有n-1个点没有遍历
      t=-1
      for j in range(1,n+1):
         if st[j]==0 and (t==-1 or dist[t]>dist[j]):  #找到没处理过得最小距离点
            t=j
      for j in range(1,n+1):
         dist[j]=min(dist[j],dist[t]+gra[t][j])  # t-j的距离,找最小值
      st[t]=1  # 标记处理过
   return dist[n]
      
n,m=map(int,input().split())
 #下标全部转为从1开始
stay=[0]+list(map(int,input().split()))
stay[n]=0   
gra = [[float('inf')] * (n+1) for _ in range(n+1)]
dist = [float('inf')] * (n+1)
st=[0]*(n+1)  # 标志是否处理
 
 
for i in range(m):
   u,v,w=map(int,input().split()) #这里重构图
   gra[u][v]=stay[v]+w
   gra[v][u]=stay[u]+w
 
 
print(dij())
   

4. Bellman-Ford algorithm

4.1 Algorithm Introduction

Single-source shortest path problem: Given a starting point s, find the shortest path from it to all n nodes in the graph.

 4.2 Algorithm templates and examples

4.2.1 Business trip issues

 Bellman - Ford implementation

import sys
import collections
import itertools
import heapq   # 默认小顶堆
import math
sys.setrecursionlimit(300000)

n,m = map(int,input().split())
t=[0]+list(map(int,input().split()))  # 从t=1开始
e=[]  # 数组存边
for i in range(1,m+1):
    a,b,c = map(int,input().split())
    e.append([a,b,c])
    e.append([b,a,c])    # 双向边

dist=[2**64]*(n+1)  # 存储到终点的距离
dist[1]=0
for k in range(1,n+1):  # 最大循环n次,即n个点
    for a,b,c in e:  # 检查每条边
        res=t[b]  # b的隔离时间
        if b==n:
            res=0
        dist[b]=min(dist[b],dist[a]+c+res)   # 问邻居是否能到达起点
print(dist[n])

 Dijstra implementation

import sys  #设置递归深度
import collections  #队列
import itertools  # 排列组合
import heapq  #小顶堆
import math
sys.setrecursionlimit(300000)
 
 
 
def dij():
   dist[1]=0  #很重要
   for _ in range(n-1): # 还有n-1个点没有遍历
      t=-1
      for j in range(1,n+1):
         if st[j]==0 and (t==-1 or dist[t]>dist[j]):  #找到没处理过得最小距离点
            t=j
      for j in range(1,n+1):
         dist[j]=min(dist[j],dist[t]+gra[t][j])  # t-j的距离,找最小值
      st[t]=1  # 标记处理过
   return dist[n]
      
n,m=map(int,input().split())
 #下标全部转为从1开始
stay=[0]+list(map(int,input().split()))
stay[n]=0   
gra = [[float('inf')] * (n+1) for _ in range(n+1)]
dist = [float('inf')] * (n+1)
st=[0]*(n+1)  # 标志是否处理
 
 
for i in range(m):
   u,v,w=map(int,input().split()) #这里重构图
   gra[u][v]=stay[v]+w
   gra[v][u]=stay[u]+w
 
 
print(dij())
   

  

5. SPFA algorithm

5.1 Algorithm Introduction

Improved Bellman-Ford Algorithm

 5.2 Algorithm steps

5.3 Algorithm examples and templates

5.3.1 The shortest path problem under random data

import sys
import collections
import itertools
import heapq   # 默认小顶堆
import math
sys.setrecursionlimit(300000)


def spfa(s):
    dis[s]=0
    hp=[]
    heapq.heappush(hp,s)
    inq=[0]*(n+1)   # 判断是否在队列中
    inq[s]=1
    while(hp):
        u=heapq.heappop(hp)
        inq[u]=0
        ''' 下面两句认为没用,因为队列中的u都是因为上一个结点的邻居更新后
            放进来的,删了之后一样AC '''
        if dis[u]==inf:   #到起点的距离为无穷大,没有必要更新邻居
            continue
        for v,w in e[u]:    # 遍历u的邻接表
            if dis[v]>dis[u]+w:
                dis[v]=dis[u]+w
                if(inq[v]==0):  # 状态有更新,v的邻居可以通过他得到更近路径
                    heapq.heappush(hp,v)
                    inq[v]=1
n,m,s = map(int,input().split())
e=[[] for i in range(n+1)]  # 临接表存边
inf=2**64
dis=[inf]*(n+1)
for i in range(m):  # 读边
    u,v,w = map(int,input().split())
    e[u].append((v,w))   # 邻接表存边,有向图
spfa(s)
for i in range(1,n+1):
    if dis[i]>=inf:
        print('-1',end=' ')
    else:
        print(dis[i],end=' ')

 5.4 Comparison of Dijstra and SPFA

6. Minimum spanning tree algorithm

6.1 Prim's Algorithm

6.1.1 Building roads (example template)

import sys
import collections
import itertools
import heapq   # 默认小顶堆
import math
sys.setrecursionlimit(300000)


def prim():
    ans,cnt=0,0   # cnt 是加入MST的点的数量
    q=[]
    vis=[0 for i in range(n+1)]   # 1 表示点在MST中
    heapq.heappush(q,(0,1))
    while q and cnt<n:
        w,u = heapq.heappop(q)   # 出距离集合最近的点
        if  vis[u] !=1:  # 不再MST中
            vis[u]=1
            ans+=w
            cnt+=1
            for v,w in e[u]:   # 遍历点u的邻居,边长为w
                heapq.heappush(q,[w,v])  # 加入MST的点的数量不等于n,说明原图不连通
    if cnt!=n:   # 加入MST的点的数量不等于n,说明原图不连通
        print('-1')
    else:
        print(ans)

        
n,m = map(int,input().split())
e=[[] for i in range(n+1)]
for i in range(m):
    u,v,w  =map(int,ipnut().split())
    e[u].append((v,w))  # u的邻居是v,边长w
    e[v].append((u,w))  # 双向边

prim()

6.2 Krusal Algorithm

Realized by union check

 6.2.1 Example template (road construction)

import sys
import collections
import itertools
import heapq   # 默认小顶堆
import math
sys.setrecursionlimit(300000)


def find(x):
    if s[x] ==x:
        return x
    s[x]=find(s[x])  # 路径压缩
    return s[x]

def merge(x,y):
     s[find(y)]=find(x)
def kruskal():
    cnt=0
    ans=0
    e.sort(key=lambda x: x[2])  # 将边从小到大排序
    for i in range(n+1):   # 并查集初始化
       s.append(i)
    for i in range(m):  # 遍历所有边
        x,y=e[i][0],e[i][1]
        e1,e2 =find(x),find(y)
        if e1==e2:  # 属于同一个集,要这条边的话产生圈
            continue
        else:
            ans+=e[i][2]
            merge(x,y)
            cnt+=1
        if cnt==n-1:
            break
    if cnt!=n-1:   # 边的数量不等于n-1,说明有点不再MST上面
        print(-1)
    else:
        print(ans)
    return
e=[]  # 数组存边
s=[]  # 并查集
n,m=map(int,input().split())
for i in range(m): # 存m条边
    u,v,w = map(int,input().split())
    e.append((u,v,w))   # 存边
kruskal()

6.3 Comparison of two algorithms

7. Four solutions to a national competition problem

import os
import sys
import itertools
import heapq

# Bellman-Ford O(mn)
'''
n,m=map(int,input().split())
stay=[0]+list(map(int,input().split()))
stay[n]=0  # 终点不需要隔离
e=[]
for i in range(m):
  a,b,c=map(int,input().split())
  e.append([a,b,c])
  e.append([b,a,c])

dp=[sys.maxsize for i in range(n+1)] 
dp[1]=0  #到起点的距离为0
def Bellman_ford():
  for i in range(1,n+1): # n个点
    for j in e:  # m条边
      v1,v2,cost=j
      #if v2==n:  # n号城市不需要隔离时间
      dp[v2]=min(dp[v2],dp[v1]+cost+stay[v2])
Bellman_ford()
print(dp[n])
'''


# Dijstra  临接表写法 O(n*n)
'''
n,m=map(int,input().split())
stay=[0]+list(map(int,input().split()))
stay[n]=0  # 终点不需要隔离
edge=[[]for i in range(n+1)]
for i in range(m):
  a,b,c=map(int,input().split())
  edge[a].append([b,c+stay[b]])
  edge[b].append([a,c+stay[a]])

hp=[]
vis=[0 for i in range(n+1)]
dp=[sys.maxsize for i in range(n+1)]
dp[1]=0  # 自身到自身距离为0
def dijstra():
  heapq.heappush(hp,(0,1)) 
  while hp:
    u=heapq.heappop(hp)[1]
    if vis[u]:
      continue
    vis[u]=1
    for i in range(len(edge[u])): # 遍历u的边
      v,w=edge[u][i][0],edge[u][i][1]
      if vis[v]:
        continue
      if dp[v]>dp[u]+w:
        dp[v]=dp[u]+w
        heapq.heappush(hp,(dp[v],v))    
dijstra()

print(dp[n])
'''


# Dijstra临接矩阵写法  O(n*n)
'''
n,m=map(int,input().split())
stay=[0]+list(map(int,input().split()))
stay[n]=0  # 终点不需要隔离
edge=[[sys.maxsize for i in range(n+1)]for i in range(n+1)]
for i in range(m):
  a,b,c=map(int,input().split())
  edge[a][b]=c+stay[b]
  edge[b][a]=c+stay[a]

vis=[0 for i in range(n+1)]
dp=[sys.maxsize for i in range(n+1)]
dp[1]=0  # 自身到自身距离为0

def dijstra():
  for i in range(n-1):   #只需要处理n-1个点
    t=-1
    for j in range(1,n+1):   # 找到没处理过得并且距离最小的
      if vis[j]==0 and(t==-1 or dp[j]<dp[t] ):
        t=j
    for j in range(1,n+1):
      dp[j]=min(dp[j],dp[t]+edge[t][j])
    vis[t]=1
dijstra()
print(dp[n])
'''

# SPFA
'''
n,m=map(int,input().split())
stay=[0]+list(map(int,input().split()))
stay[n]=0  # 终点不需要隔离
edge=[[]for i in range(n+1)]   #临接表存边
for i in range(m):
  a,b,c=map(int,input().split())
  edge[a].append([b,c+stay[b]])
  edge[b].append([a,c+stay[a]])
  
dp=[sys.maxsize for i in range(n+1)]
dp[1]=0  # 自身到自身距离为0


def spfa():
  hp=[]
  heapq.heappush(hp,1)   #用堆实现相当于优先队列,加快一点效率罢了
  in_hp=[0 for i in range(n+1)]  # 标记数组换为了判断是否在队列中 
  in_hp[1]=1  # 1在队列
  while hp:
    u=heapq.heappop(hp)
    in_hp[u]=0  # 标记为不在队列
    if dp[u]==sys.maxsize:   # 没有处理过的点,直接跳过,只处理邻居点
      continue
    for i in range(len(edge[u])): # 遍历u的边
      v,w=edge[u][i][0],edge[u][i][1]
      if dp[v]>dp[u]+w:
        dp[v]=dp[u]+w
        if in_hp[v]==0: # 他的邻居不再队列,就把他入队,方便下下次更新邻居的邻居结点
          heapq.heappush(hp,v)    
          in_hp[v]=1
spfa()
print(dp[n])
'''

Guess you like

Origin blog.csdn.net/weixin_52261094/article/details/130477269