“创作背景”
本人在上个周末经历了社死,于是想到了写这篇东西
“少社死优先”
在开始之前,我们先看一下一些会一直用到的概念
- 社死值:指一个人在某一条路径上感到社死的程度(在本文中指某一条路径上的人数)
- 最短路:在本文中指从起点通往目的地的社死值最小的路径(本文就是要求这个东西)
- 路径:只包含两个节点的一条路
- 道路:包含很多节点的一条路
“问题情境”
现在我们有一张地图(这里只是随手画了一张)
现在一位仁兄他站在起点的位置,要去终点(起点和终点的位置都是不确定的)。节点之间有很多路径,但是呢他是一名社恐晚期患者,只要路上有人就会感到社死,而且路上的人越多他感到社死的程度(即社死值)就越大。现在他知道节点之间一共有多少条路径,哪些节点之间是有路径的,以及每条路径上都有多少人,求能够到达终点的、让他感到社死程度最低的道路
(一条道路的社死值=这条道路中包含的路径的社死值的总和)
这个问题可以理解为一个最短路问题,本文中会介绍广度优先搜索解决最短路问题(以及上面这位仁兄的人生难题)的方式
广度优先搜索的运行过程
在广度优先搜索中,每一个节点都可以看作是一个对象。要实现广度优先算法,首先需要一个队列,以及一个可以用来表示节点的数据类型。
以上条件都具备了之后呢,我们一起欣赏一下广度优先搜索算法的运行过程
- 定义一个节点,用来表示起点,然后将这个节点入队
- 然后写一个while循环,在队列不为空的情况下执行以下步骤:
- 取队头元素
- 判断是否已经到达了终点
- 如果已经到达了终点,那么就将这条道路的社死值与原有的最小社死值进行比较。如果原有的最小社死值大于这条道路的社死值,那么就更新最短路径和最小的社死值
- 如果没有到达终点,那么就寻找当前节点可以通向的节点(但是找到的节点不能包括在原有的道路中,否则会造成循环)
- 将找到的节点也做成一个对象,然后入队(此部分会在代码里详细写到)
- 将队头元素出队
准备工作
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='')