蓝桥杯第16天(Python版)(图论)

本文介绍了计算中位数的一种方法,以及几种常见的图论算法,包括Floyd的多对多最短路径算法、Dijkstra的一对多最短路径算法,并讨论了最小生成树的构建,同时提到了并查集在解决路径问题中的应用。此外,还提及了堆在求解最短路径问题中的作用和动态规划的基本原理。
摘要由CSDN通过智能技术生成

1. 最大降雨量

 问题是求中位数的中位数,上面解题思路图便于理解

import os
import sys

# 请在此输入您的代码
# 降雨量为 7 周能量的中位数。
#1-7
#8-14
#15-21
#22-28
#29-35
#36-42
#43-49
#22-49 =28
# 后面28天尽可能的相同,这样中位数才能大


# 22+49 23+48  24+47  34
# 25+46 26+45  27+44  35
# 28+43 29+42  30+41  36
# 31+40 32+39  33+38  37

print(34)

2.最短路算法比较

3.Floyd算法 (多对多,可以简化成一对多或者多对一)

动态规划 ,原理

 计算过程,结合图形理解

 计算步骤(将k从1扩展到n)

 数组滚动简化方程 (细看,k只用到了上一层,所以可以简化,直接舍去不要)

def floyd():
    global dp
    for k in range(1,n+1):
        for i in range(1,n+1):
            for j in range(1,n+1):
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k][j])
dp = [[99999999 for _ in range(N) for _ in range(N)]  #dp[i,j]表示i到j最短路径

Floyd例题1

两种写法


import math
def floyd():
    global dp
    for k in range(1,2022):
        #for i 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])
dp = [[9999999999 for _ in range(2022)] for _ in range(2022)]  #dp[i,j]表示i到j最短路径

for i in range(1,2022):
    for j in range(1,2022):
        if abs(i-j)<=21:
            dp[i][j]=i//math.gcd(i,j)*j
floyd()
print(dp[1][2021])
from math import gcd
def lcm(x,y):
    return x//gcd(x,y)*y
dp = [9999999999]*2022      # 点i到1的最短路径
dp[1]=0  #到自身的距离为0
for i in range(1,2022):  # 1-2022
    for j in range(i+1,i+22):  #和i有边的点j
        if j>2021:
            break
        dp[j] = min(dp[j],dp[i]+lcm(i,j)) # 更新最短路

print(dp[2021])

4.Dijkstra

算法实现

举例说明:

 

 例题:

 

 

import heapq  #导入堆,默认是小顶堆
def dij(s):
    done = [0 for i in range(n+1)]  # A:已确定为最短路径的点
    hp=[]   # 创建一个空堆
    dis[s]=0 #到自身的距离
    heapq.heappush(hp,(0,s))   # B:A的点向外扩散的邻居点,在堆中根据距离排序(从小到大)
    while hp:
        u = heapq.heappop(hp)[1] # 弹出最小元素
        if done[u]:  #表示被处理过了,起到了一个去重,更新小值的作用 
            continue
        done[u]=1
        for i in range(len(G[u])): #遍历u的邻居,把u的邻居放入B
            v,w = G[u][i]   # 邻居v,边长w
            if done[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**50
dis = [INF]*(n+1)  # 存储距离
for i in range(m): # 读m条边,这里是单向边
    u,v,w = map(int,input().split())
    G[u].append((v,w))  # 存图,u的一个邻居是v,边长是w

dij(s)
for i in range(1,n+1):
    if dis[i]>=INF:print("-1",end=' ')
    else:
        print(dis[i],end=' ')

5.堆的知识补充 

 

 6.最小生成树

 

 

 通过并查集实现最小生成树的构建,n个点那么最小生成树有n-1条边,当生成的树有n-1条边时跳出构建

 

聪明的猴子问题

 

路径问题

 

import os
import sys

# 请在此输入您的代码
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+1,i+22):  # 注意边界
    if j >2021: break
    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

蓝桥王国问题

import heapq  # 导入堆
def dij(s):
    done=[0 for i in range(n+1)]  # 记录是否处理过
    hp=[]  #堆
    dis[s]=0
    heapq.heappush(hp,(0,s))   #入堆,小顶堆
    while hp:
        u=heapq.heappop(hp)[1] #出堆元素结点
        if done[u]: #当前结点处理过
            continue
        done[u]=1
        for i in range(len(G[u])): #遍历当前结点的邻居
            v,w =G[u][i]
            if done[v]:continue
            dis[v]=min(dis[v],dis[u]+w)  # 更新当前结点邻居的最短路径
            heapq.heappush(hp,(dis[v],v))




n,m = map(int,input().split())#
s=1  # 从1开始访问
G=[[]for i in range(n+1)]   #邻接表存储
inf = 2**50
dis = [inf]*(n+1) #存储距离
for i in range(m):# 存边,这里是单向边
    u,v,w = map(int,input().split())
    G[u].append((v,w))  #记录结点u的邻居和边长

dij(s)
for i in range(1,n+1):
    if dis[i]==inf:
        print("-1",end=' ')
    else:
        print(dis[i],end=' ')

蓝桥公园 问题

import os
import sys

# 请在此输入您的代码
#floyd算法,多对多


def floyd():
  global dp
  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)
   

出差(Dijstra的矩阵形式实现)

import sys
#from typing import List

n, m = map(int, input().split())

# 邻接矩阵
gra = [[float('inf')] * (n+1) for _ in range(n+1)]
dist = [float('inf')] * (n+1)
g = [0] + list(map(int, input().split()))
g[n] = 0
st = [False] * (n+1)  # 标志是否被处理过

#  矩阵形式存储实现Dijkstra
def dijkstra():
    dist[1] = 0   #到自身距离为0
    for _ in range(n - 1): # 循环n-1次,因为还剩下n-1个点
        t = -1  #防止出现索引出界问题
        for j in range(1, n + 1):  #找到没处理过得最小最小距离点
            if not st[j] 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])
        st[t] = True # 标记为处理过了
    return dist[n]

for _ in range(m):
    u, v, c = map(int, input().split())
    gra[u][v] = g[v] + c
    gra[v][u] = g[u] + c

print(dijkstra())

主要掌握(Floyd(多对多(矩阵形式存储)),Dijstra(1对多(邻接表,矩阵))两个算法)

最小生成树问题

 熟悉并查集的使用

 

import math
def set_find(x):
    if x==_set[x]:
        return _set[x]
    else:
        _set[x]=set_find(_set[x])
        return _set[x]
    
def set_merge(x,y):
    if set_find(x)!=set_find(y): #需要合并
        #_set[x]=set_find(y)  # 合根指向y的根,这里有问题,即可能只改了一个的指向,要改这个连通块的根结点的指向
        _set[set_find(x)]=set_find(y)


       
_set=[i for i in range(1023)]  # 并查集初始化

n1=int(input())  # 猴子个数
m=list(map(int,input().split()))  # 读入最大跳入距离
n2=int(input())  #树的个数
location =[0]  #方便从1开始
edge_list=[]  #存储树的坐标


for i in range(n2):  # 存储树的坐标
    location.append(list(map(int,input().split())))

# 转换为图,记录每一条边
for i in range(1,n2):
    for j in range(i+1,n2+1):
      w=math.sqrt((location[i][0]-location[j][0])**2+(location[i][1]-location[j][1])**2)
      edge_list.append((i,j,w))  #按照 ( i,j,w ) 形式存储每一条边

# 开始生成最小生成树
maxvalue=0  # 记录生成树的最长边
num=0  #判断已经处理了多少条边
edge_list.sort(key=lambda x:x[2])  # 按边长从小到大排序
for i in  edge_list:
    if set_find(i[0])!= set_find(i[1]):
        set_merge(i[0],i[1])
        maxvalue = max(maxvalue,i[2])
        num+=1  # 处理了一条边
        if num==n2-1: #构建好了n-1条边,完成
            break

ans =0
for i in m:
    if i>=maxvalue:
        ans+=1
print(ans)

官方标答

import math

class Edge:
    x = 0
    y = 0
    w = 0.0
    def __init__(self, x, y, w):
        self.x = x
        self.y = y
        self.w = w

def find(x):
    if f[x]==x:
        return f[x]
    else:
        f[x] = find(f[x])
        return f[x]

def merge(x,y):
    xx = find(x)
    yy = find(y)
    if xx!=yy:
        f[yy] = xx

if __name__ == '__main__':
    m = int(input())
    a = list(map(int, input().split()))
    n = int(input())
    x = [0 for i in range(n + 2)]
    y = [0 for i in range(n + 2)]
    for i in range(n):
        b = list(map(int, input().split()))
        x[i + 1] = b[0]
        y[i + 1] = b[1]
    edge_list = []
    maxvalue = 0
    num = 0
    for i in range(1, n + 1):
        for j in range(i + 1, n + 1):
            w = math.sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]))
            edge =  Edge(i, j, w)
            edge_list.append(edge)

    edge_list.sort(key=lambda x: x.w)
    f = [i for i in range(n + 1)]
    for i in edge_list:
        if find(i.x)!=find(i.y):
            merge(i.x,i.y)
            maxvalue = max(maxvalue,i.w)
            num+=1
            if num==n-1:
                break
    ans = 0
    for i in range(m):
        if a[i]>=maxvalue:
            ans+=1
    print(ans)

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值