BUUCTF_Crypto题目:救世捷径(dijstra算法)

BUUCTF刷题Crypto篇

这是一道密码学题目但好像……没考到密码学?基本思路就是找到最短路径然后将最短路径上的内容连起来组成flag。dijstra算法学了多少遍了还是学一遍忘一遍QAQ



一、题目

题目:救世捷径
题目已知文件


二、解题思路

txt文件中每行的前两个数字作为无向图的顶点,第三个数字是两顶点之间的距离,最后的字符串是两顶点之间的内容,将起点到终点最短路径经过的边上的内容组合起来便是flag。
单源点最短路径算法:dijstra算法。
一些想到哪是哪的tips写在这里咯:

1.一些前期的初始化和数据处理

1)初始化各点之间的距离为“无穷远”(在程序中用一个比较大的数代替这个无穷远的概念),一般可以直观地想出用27*27的二维数组存这些距离值(Python中是用list套list作为过去高级语言中二维数组的那种存在……而且要注意要初始化把list里面都放上东西才行!),之后我们就操作索引是1-26的那些元素,浪费掉位置0处的空间,但可以恰好对应顶点1-26,清晰明了~
2)按行读取题目txt文件中的内容,用的是readlines(),得到的数据形式是每行作为一个元素组成的list。然后用strip()去掉行尾的换行符’\n’,再用split(’ ')将每行内容按空格分割组成新的list,方便后面在程序中的调用。
3)因为在2)步中已经分割出了每行的元素,就可以用2)步中的数据去初始化1)步中27*27的list中的数据,把已知的那些两点之间的距离放入即可,具体写法见程序代码。

2.实现dijstra算法的函数

1)初始化一个长度是27,元素全是0xffff(代表距离很远)的list,用于记录当前顶点(索引与顶点序号一致是1-26)对于顶点1的最短距离。
2)初始化一个长度为27,元素全是0的list,元素值用于记录当前顶点(索引与顶点序号一致是1-26)是否已经找到了距离顶点1的最短路径,确定了最短路径就置该顶点序号对应索引值的元素为1。后面将这里元素值是1的顶点称为“已经确定的集合”。每次更新完各顶点到顶点1的距离后,找到最短的一个,将该顶点位置元素置1,该顶点就不再参与后续的遍历。
3)初始化一个长度为27,元素全是1的list,用于记录当前顶点到顶点1的最短路径的前驱顶点,用于最后回溯路径。
过程:
首先找到和顶点1直连的顶点,找到这些顶点中距离顶点1最短的一个顶点,将该顶点加入“已经确定的集合“,遍历该顶点的邻接顶点,更新顶点1到各个邻接顶点的最短距离。再找到现在与顶点1距离最短的顶点(在”已经确定的集合“中的顶点就不再遍历),再去遍历该顶点的邻接顶点,更新顶点1到这些邻接顶点的最短距离,从中找到距离最短的顶点加入“已经确定的集合”,再遍历该顶点的邻接顶点,更新这些顶点与顶点1的最短距离,找到与顶点1距离最短的顶点……以此循环直至所有顶点都加入“确定的集合”。
核心思想:
每次循环都找到当前距离顶点1最近的一个顶点,判断路径中经过该顶点后再到达与其邻接的其他顶点的距离,是否比之前存储的这些顶点到顶点1的距离更短,如果更短就更新对应顶点到顶点1的最短距离,更新完后再找到与顶点1距离最短的顶点重复上述操作。

三、Python代码实现

强迫症一样的爱写注释,主要是菜QAQ

graph=[]
for i in range(27):
    graph.append([]) #在一个list中放27个list,索引0-26
for i in range(27):
    for j in range(27):
        graph[i].append(0xffff) #先将图中各个顶点之间的距离初始化为一个比较大的数
f=open('./Downloads/dij.txt','r').readlines()  #按行读取,每行内容是list中的一个元素,全部内容组成一个整体的list
#这里需要先手动将txt文件中的最后一行换行去掉否则会多一个'\n'
#print(f)
li=[]
for x in f:
    li.append(x.strip().split(' ')) #strip()删除字符串前后空格,这里是去掉了最后的换行符'\n',然后再按' '分割每行的每个元素,原本在同一子list中的一行元素也彼此独立出来
#print(li)
for x in li:
    graph[int(x[0])][int(x[1])]=int(x[2])
    graph[int(x[1])][int(x[0])]=int(x[2])

def try_dijstra():
    min_d=[0xffff for i in range(27)]  #记录点i到起点1的最短距离
    route=[1 for i in range(27)]  #记录前驱顶点
    isSure=[0 for i in range(27)]  #记录各点到起点距离是否已经确定
    for i in range(2,27):
        min_d[i]=graph[i][1]  #初始化一下能直连1的顶点和1的距离
    min_d[1]=0
    isSure[1]=1

    for i in range(26):
        min=0xfffff
        temp=-1
        for j in range(2,27): # 找到当前离顶点1最近的顶点加入已经确定的“顶点阵营”
            if isSure[j]==0 and min>min_d[j]:
                min=min_d[j]
                temp=j
        isSure[temp]=1
        for j in range(2,27):# 判断从顶点1开始,在经过该顶点后,再到达其邻接顶点的距离,是否比其邻接顶点原本到顶点1的距离更近,如果更近就更新最短距离
            if min_d[j]>min_d[temp]+graph[temp][j]:
                min_d[j]=min_d[temp]+graph[temp][j]
                route[j]=temp
    return (route,min_d)

route,min_d=try_dijstra()
print(min_d[26]) #最短距离
print(route) #前驱顶点

passv=[]  #存放顶点之间的“内容”(内容最后要组成flag
for i in range(27):
    passv.append([]) # 还是在一个list中放27个list
for i in range(27):
    for j in range(27):
        passv[i].append(0)  #需要将内部list中初始化出27个“位置”否则会报错索引越界
for x in li:
    passv[int(x[0])][int(x[1])]=x[3]
    passv[int(x[1])][int(x[0])]=x[3]

y=26
l=[]
while y!=1:
    print(y) #输出终点到起点的最短路径经过的顶点
    l.append(passv[y][route[y]]) #y到其前驱顶点route[y]之间的内容
    y=route[y]
print()
l=l[::-1]
for i in range(len(l)):
    print(l[i],end='')

'''
339
[1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 5, 5, 5, 4, 4, 4, 6, 6, 6, 25, 9, 11, 12, 6, 18, 22, 25]
26 25 22 12 5 2 
FLAG{WEIVKASJVLSJCHFSJVHJSDEV}
'''


总结

明明很经典看过很多遍的算法,还是写不出代码呢,哎

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值