洛谷图论训练python代码

176 阅读2分钟

邮递员送信

链接:www.luogu.com.cn/problem/P16…

Dijkstra:

Dijkstra的重点在于把每个点到某一个点的最短距离纪录下来,更新距离的过程就可以用一个字典来存边,比如出发的点为字典的keys,之后依次遍历就好了,而每次都用最短距离的点进行更新,但是这个题目还需要返回,就需要加入图论中的翻转操作.假设要问从x到1的最短路,为x->a->b->c->1,也就是说x->a,a->b,b->c,c->1都有路可走,那么我们想想,从x开始x->a,a->b,b->c,c->1的最短路不就是从1开始1->c,c->b,b->a,a->x的最短路吗?于是这时,我们把x->a,a->b,b->c,c->1这4条路径变为1->c,c->b,b->a,a->x,然后从1开始跑最短路,而它们的最短路是一样的。

code

#Dijkstra算法
from collections import  defaultdict
sumvalue=0
def Dijkstra(road_dict):
    global sumvalue
    dist = [float('inf') for node in range(n + 1)]  # 存储最小路径
    seen = [0 for i in range(n + 1)]  # 标记数组初始化
    dist[1] = 0  # 自身已经求出最短距离,进行计算
    while True:
        next_node = 0  # 首先初始化的时候就代表不能找到最小的
        for i in range(1, n + 1):  # 遍历n个点,然后依次用
            if not seen[i] and dist[i] < dist[next_node]:  # 找到这个里面最小的
                next_node = i  # 最短的目的地就是i说明
        if next_node == 0:  #如果没有找到就说明都已经遍历完或者没有更多的节点
            break
        seen[next_node] = 1
        for k, l in road_dict[next_node]:  # 使用字典的原因就是可以直接找到相应的节点边
            dist[k] = min(dist[k], dist[next_node] + l)  # 也
    for i in range(2, n + 1):  # 需要计算出
        sumvalue += dist[i]  #
​
n,m = map(int,input().split()) #n为节点个数,m为边个数
road_dict =defaultdict(list)    #默认为list的字典
road_reverse_dict=defaultdict(list) #反向建立边
for i in range(m):
    u,v,w = map(int,input().split())        #使用了u,v,w
    road_dict[u].append((v,w)) #去的路上路径
    road_reverse_dict[v].append((u,w)) #反向建立边
​
Dijkstra(road_dict)
Dijkstra(road_reverse_dict)
​
​
print(sumvalue)

Job Hunt S

www.luogu.com.cn/problem/P19…

因为这个里面需要求最长路,于是将其处理成求负权的最小值,使用spfa算法(链接:

搭配购买

www.luogu.com.cn/problem/P14…

这道题其实就是并查集与01背包的组合题,首先使用并查集将所有的物品价值和价格加起来.然后看成一种物品进行背包问题求解即可.

import collections
​
father_dict = {}
size_dict = {}
​
def init(n):
    global father_dict
    for i in range(1,n+1):
        father_dict[i] = i
        size_dict[i]=1 #优先度为1
​
def union(x,y): #将两个集合并起来
    global father_dict,size_dict
    x = find(x)
    y = find(y)
    if(x!=y):   #两个集合不是一样的话
        size_dict_x = size_dict[x]
        size_dict_y = size_dict[y]
        if(size_dict_x>=size_dict_y):
            father_dict[y] = x
            size_dict[x] += size_dict_y
        else:
            father_dict[x] = y
            size_dict[y] +=  size_dict_x
def find(x):
    global father_dict
    root = x
    while(root!=father_dict[root]):
        root = father_dict[root]            #一直寻找其父节点
    while(x != root):       #一直更新
        t = father_dict[x]              #更新所在的集合
        father_dict[x] = root
        x = t
    return root
​
n,m,w =map(int,input().split()) #也就是使用三个数字
init(n)
weights =[0 for i in range(n+1)]
price = [0 for i in range(n+1)]
for i in range(1,n+1):
    c,d =map(int,input().split())
    weights[i] = d
    price[i] = c
​
for j in range(m):
    u,v = map(int,input().split())
    union(u,v) #联合u,v
​
for i in range(1,n+1): #
    if(father_dict[i]!=i):  #如果相等的话就没必要比较了
        weights[father_dict[i]]  += weights[i]
        weights[i] = 0
        price[father_dict[i]]  += price[i]
        price[i] = 0
     #为何不能列表相加#接下来进行dp
dp = [0 for i in range(w+1)]  #有很多钱
for i in range(1,n+1):
    for j in range(w,price[i]-1,-1):  #w为最大值,从最大值遍历到最小值
            dp[j] = max(dp[j-price[i]]+weights[i],dp[j]) #也就是如果用这个更大了的话
​
print(dp[w])

\