从社死到广度优先搜索

“创作背景”

本人在上个周末经历了社死,于是想到了写这篇东西

“少社死优先”

在开始之前,我们先看一下一些会一直用到的概念

  1. 社死值:指一个人在某一条路径上感到社死的程度(在本文中指某一条路径上的人数)
  2. 最短路:在本文中指从起点通往目的地的社死值最小的路径(本文就是要求这个东西)
  3. 路径:只包含两个节点的一条路
  4. 道路:包含很多节点的一条路

“问题情境”

现在我们有一张地图(这里只是随手画了一张)
在这里插入图片描述
现在一位仁兄他站在起点的位置,要去终点(起点和终点的位置都是不确定的)。节点之间有很多路径,但是呢他是一名社恐晚期患者,只要路上有人就会感到社死,而且路上的人越多他感到社死的程度(即社死值)就越大。现在他知道节点之间一共有多少条路径,哪些节点之间是有路径的,以及每条路径上都有多少人,求能够到达终点的、让他感到社死程度最低的道路
(一条道路的社死值=这条道路中包含的路径的社死值的总和)
这个问题可以理解为一个最短路问题,本文中会介绍广度优先搜索解决最短路问题(以及上面这位仁兄的人生难题)的方式

广度优先搜索的运行过程

在广度优先搜索中,每一个节点都可以看作是一个对象。要实现广度优先算法,首先需要一个队列,以及一个可以用来表示节点的数据类型。
以上条件都具备了之后呢,我们一起欣赏一下广度优先搜索算法的运行过程

  1. 定义一个节点,用来表示起点,然后将这个节点入队
  2. 然后写一个while循环,在队列不为空的情况下执行以下步骤:
  3. 取队头元素
  4. 判断是否已经到达了终点
  5. 如果已经到达了终点,那么就将这条道路的社死值与原有的最小社死值进行比较。如果原有的最小社死值大于这条道路的社死值,那么就更新最短路径和最小的社死值
  6. 如果没有到达终点,那么就寻找当前节点可以通向的节点(但是找到的节点不能包括在原有的道路中,否则会造成循环)
  7. 将找到的节点也做成一个对象,然后入队(此部分会在代码里详细写到)
  8. 将队头元素出队

准备工作

pip3 install numpy#Linux/MacOS
pip install numpy#Windows
import numpy as np

定义一些全局变量

result_path=''#最短路
result_embarrassment=0xff#最小的社死值

然后要定义一个节点,代表一个地点

class Node:
	id_number=0#节点的编号
	path=''#从起点到达此节点的一条路径
	embarrassment=0#按照path中存储的路到达该节点之后的社死值总和

因为我们采用广度优先搜索,所以需要一个队列

class Queue(list):#这边直接从list继承过来了
	def init_queue(self):#初始化队列
		self.clear()
	def push(self,item):#入队
		self.append(item)
	def pop(self):#出队
		del self[0]
	def front(self):#取队头元素
		return self[0]
	def isempty(self):#判断队列是否为空
		return bool(len(self))-1
q=Queue()

本次我们需要用到图,所以这边要定义两个邻接矩阵(至于为什么要定义两个呢,我本来是想用人数表示路径是否存在的,但人数可能是0,因此人数为0的路径就被标记为不存在了)

graph=np.array([[False]*1000]*1000)#这个矩阵存储两个节点之间是否存在路径
graph_1=np.array([[0]*1000]*1000)#这个矩阵存储两个节点之间的路径上有多少人,如果这两个节点之间没有路径那么就是0

然后我们再编写一个输入的代码

end=int(input('终点的编号:'))
path=int(input('节点之间一共有多少条路径:'))
for i in range(path):
    print('现在我们正在处理第',i+1,'条路径',sep='')
    node_1=int(input('这条路的一端的节点的编号:'))
    node_2=int(input('这条路的另一端的节点的编号:'))
    people=int(input('这条路上有多少人:'))
    graph[node_1][node_2]=True
    graph[node_2][node_1]=True
    graph_1[node_1][node_2]=people
    graph_1[node_2][node_2]=people
    print('-------------------------------------------')

主要代码

接下来就是本文主要的代码,即最短路的查找
首先要将起点入队

start=Node()
start.id_number=0
start.path='0'
start.embarrassment=0
q.push(start)

然后再使用广度优先搜索查找最短路

while not q.isempty():
    front=q.front()#取队头元素
    id_number=front.id_number#队头节点的编号
    current_path=front.path#可以走到队头节点的一条路径
    embarrassment=front.embarrassment#社死值总和
    if id_number==end:#如果已经走到了目的地
        if embarrassment<result_embarrassment:#如果社死值比最小的社死值还要小
            result_embarrassment=embarrassment#将最小的社死值更新为当前的社死值
            result_path=current_path#将最短路更新为当前的路
    available=[]#下一步可以通往的节点
    for i in range(len(graph[id_number])):
        if graph[id_number][i]:#如果可以从当前节点通往编号为i的节点
            if not str(i) in current_path:#并且我以前没有走到过编号为i的节点
                available.append(i)#将它添加到下一步可以通往的节点中
    for i in available:
        next_path=current_path+'->'+str(i)#将下一个节点的编号添加到路中
        next_embarrassment=embarrassment+graph_1[id_number][i]#社死值总和=这个节点的社死值总和+从当前节点到下一个编号为i节点的社死值
        next_id=i#下一个节点的编号为i
        new_node=Node()
        new_node.path=next_path
        new_node.embarrassment=next_embarrassment
        new_node.id_number=next_id
        q.push(new_node)#将下一个节点入队
    q.pop()#将队头元素出队

最后再输出一下

print('社死值最小的路径是', result_path, ',社死值是', result_embarrassment,sep='')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值