算法之路-图的最短路

1. 单源最短路

1.1 无负权边 - 迪杰斯特拉算法

  1. 自环和重边对迪杰斯特拉算法没有影响
  2. 要求无负权边的原因是:考虑自环中有负权边的情况
    在这里插入图片描述
    其中A 为源点,C为终点,第一轮选取源点A,结束后更新,d[c]=1,d[b]=2,第二轮选取C点,不再更新d[c],d[c]值固定为1,但实际上A到C的最短距离应该为2+(-2)=0。
算法核心:每次选取距离源点最近的点,对其余距离进行刷新,直至选取到目标点。
def djs():
    global n,m,hd,va,nx,wg
    M=0x3f3f3f
    st=[True]*(n+1)
    d=[M]*(n+1)
    d[1]=0
    for i in range(n):
        id=-1
        tmp=M
        for t in range(1,n+1):
            if st[t] and tmp>d[t]:
                tmp=d[t]
                id=t
        st[id]=False
        j=hd[id]
        while j!=-1:
            d[va[j]]=min(d[id]+wg[j],d[va[j]])
            j=nx[j]
    if d[n]<M:
        print(d[n])   
    else:
        print(-1)

n,m=map(int,input().split())
hd=[-1]*(n+1)
va=[0]*(m+1)
nx=[-1]*(m+1)
wg=[0]*(m+1)
idx=0
for _ in range(m):
    x,y,z=map(int,input().split())
    va[idx]=y
    nx[idx]=hd[x]
    hd[x]=idx
    wg[idx]=z
    idx+=1
djs()

    

1.2 有负权边

1.2.1 限制经过边数≤k - bellman算法

  1. 图中可以存在负权回路,因为已有k对循环次数进行限制。
算法核心:用刷新次数为k限制经过边数,每次刷新扫描所有边,更新d[]。
def bellman():
    global n,m,k,edge,M,d
    for i in range(k):
        import copy
        tmp=copy.deepcopy(d)
        for t in edge:
            x,y,z=t
            d[y]=min(tmp[x]+z,d[y])
    if d[n]<M/2:
        print(d[n])
    else:
        print("impossible")
n,m,k=map(int,input().split())
edge=[]
for _ in range(m):
    edge.append(list(map(int,input().split())))
M=2*(10**8)+1
d=[M]*(n+1)
d[1]=0
bellman()

1.2.2 不限制经过边数 - spfa算法

  1. 图中可以有负权边,但不可以有负权回路
  2. 可以用spfa算法判断图中是否包含负环,只需要加一个cnt用以计数即可 (判断条件≥n)
可以看成解禁版迪杰斯特拉算法。
def spfa():
    global n,m,hd,va,nx,wg
    M=2*(10**9)+1
    d=[M]*(n+1)
    d[1]=0
    q=[0]*(10**5+1)
    st=[True]*(n+1)
    hh=tt=0
    q[tt]=1
    st[1]=False
    tt+=1
    while tt-hh>0:
        #print(d[1:])
        tmp=q[hh]
        hh+=1
        st[tmp]=True
        j=hd[tmp]
        while j!=-1:
            if d[va[j]]>d[tmp]+wg[j]:
                d[va[j]]=d[tmp]+wg[j]
                if st[va[j]]:
                    st[va[j]]=False
                    q[tt]=va[j]
                    tt+=1
            j=nx[j]
    if d[n]<M/2:
        print(d[n])
    else:
        print("impossible")

n,m=map(int,input().split())
hd=[-1]*(n+1)
va=[0]*(m+1)
nx=[-1]*(m+1)
wg=[0]*(m+1)
idx=0
for _ in range(m):
    x,y,z=map(int,input().split())
    va[idx]=y
    nx[idx]=hd[x]
    hd[x]=idx
    wg[idx]=z
    idx+=1

spfa()

2.多源最短路-弗洛伊德算法

  1. 图中可能存在重边和自环,边权可能为负数。
  2. 图中不能存在负权回路
算法核心:以每一个点当做中介点,更新距离。
M=0x3f3f3f
n,m,k=map(int,input().split())
e=[[M]*(n+1) for _ in range(n+1)]
for i in range(n+1):
    e[i][i]=0
for _ in range(m):
    x,y,z=map(int,input().split())
    e[x][y]=min(e[x][y],z)
for i in range(1,n+1):
    print(i," as medium:")
    for j in range(1,n+1):
        for t in range(1,n+1):
            print("update e[",j,"][",t,"]",end='')
            if e[j][t]>e[j][i]+e[i][t]:
                e[j][t]=e[j][i]+e[i][t]
            print("=",e[j][t])
for _ in range(k):
    x,y=map(int,input().split())
    if e[x][y]<M/2:
        print(e[x][y])
    else:
        print("impossible")



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值