[蓝桥杯] 网络寻路 python 满分解法
前言
本人小白一枚,最近在准备python组的蓝桥杯,在此记录一下真题——网络寻路的满分解法~
方法一:回溯法
思路:
首先,我想到的第一种方法是回溯法。基本思路为先通过输入通过数据结构记录图的结构。然后以每一个元素为起点进行遍历。同时在这个过程中要保证每一条边不能重复出现,顶点只可以在开始的节点和结束的节点重复出现…,具体的思路不再描述了,因为不提倡这种方法。
时间复杂度:
O(N**4), 因为在最坏的情况下,每个顶点与其余n-1个顶点都会有边进行联系。所以我们遍历4个节点的时候就相当于for 循环了4遍,即使进行剪枝,仍然改变不了复杂度为N的四次方的事实
代码
如下:
#定义边的类,to表示它的终点,source_edge表示它的源点的另外一条出边
class Edge:
def __init__(self,to=-1):
self.to=to
self.source_edge=-1
#方法一:回溯算法
## 时间复杂度O(n**4),
##因为在极端的情况下,每个顶点可能与另外的n-1条边连接。所以我们第一次遍历n的次数,第二次,第三次,第四次都是遍历n次数
#在dsf的过程中,第一保证每条边只使用一次
class Solution:
def networkRoads(self,n,m,edges) -> str:
def addEdge(u,v):
nonlocal cnt
nonlocal lines_num
edge_list[cnt].to=v
edge_list[cnt].source_edge=pre[u]
lines_num[u]+=1
pre[u]=cnt
cnt+=1
def dsf(start,val):
nonlocal flag
nonlocal temp_second
nonlocal ans_normal
nonlocal ans_singlar
nonlocal end_set
if temp_second==0:
temp_second=-1
if start in end_set and flag==0:
return
if flag==2 and val==start:
return
if flag==1:
temp_second=val
if flag==3:
if val==temp_second:
return
if lines_num[val]==1:
end_set.add(val)
ans_singlar+=1
else:
ans_normal+=1
return
if val==-1:
p=pre[start]
else:
p=pre[val]
while p!=-1:
if not used[p//2]:
q=edge_list[p].to
flag+=1
used[p//2]=True
dsf(start,q)
used[p//2]=False
flag-=1
p=edge_list[p].source_edge
return
cnt=0
lines_num=[0 for _ in range(n+1)]
pre=[(-1) for _ in range(n+1)]
edge_list=list()
for x,y in edges:
edge_list.append(Edge(y))
addEdge(x,y)
edge_list.append(Edge(x))
addEdge(y,x)
used=[False for _ in range(m)]
ans_normal,ans_singlar=0,0
flag=0
end_set=set()
temp_second=-1
for i in range(1,n+1):
dsf(i,-1)
return ans_singlar*2+ans_normal
方法二:遍历中间路径法
1.思路
没有办法,第一种的时间复杂度太高了,即使是剪枝仍然解决不了问题。所以换一种思路来看看。我们拿最后一个例子来研究一下:
如图所示为每个顶点的度。我们在方法一中,通过O(n)的时间可以记录这个图的结构,即每个节点它的度是多少,它与哪写节点相邻。于是我们突发奇想,如果我们遍历中间的两个节点试试呢?
比如,节点1和节点2。节点1的度为3,节点2的度为2,二者相交后,节点1剩下的度为3-1=2,节点2剩下的度为2-1=1.所以二者组合后的以1,2为中间两点的组合有2*1*2种,之所以在后面又乘上了一个2,是因为我们可以逆序。。。
就是这个思路,有没有感觉很巧妙~
2.时间复杂度
O(N**2)
3.代码
#方法二,时间复杂度为O(n^2)
class Edge:
def __init__(self,to=-1):
self.to=to
self.source_edge=-1
class Solution:
def networkRoads(self,n,m,edges) -> str:
def addEdge(u,v):
nonlocal cnt
nonlocal lines_num
edge_list[cnt].to=v
edge_list[cnt].source_edge=pre[u]
lines_num[u]+=1
pre[u]=cnt
cnt+=1
def twoNode(start):
nonlocal ans
p=pre[start]
while p!=-1:
q=edge_list[p].to
if q>start:
ans+=(lines_num[start]-1)*(lines_num[q]-1)
p=edge_list[p].source_edge
cnt=0
#lines_num[i]表示第i个点的边的度
lines_num=[0 for _ in range(n+1)]
pre=[(-1) for _ in range(n+1)]
edge_list=list()
for x,y in edges:
edge_list.append(Edge(y))
addEdge(x,y)
edge_list.append(Edge(x))
addEdge(y,x)
ans=0
for i in range(1,n+1):
twoNode(i)
return ans*2
if __name__=='__main__':
solution=Solution
n,m=map(int,input().split())
edges=list()
for i in range(m):
node1,node2=map(int,input().split())
edges.append((node1,node2))
## n,m=3,3
## edges=[(1,2),(2,3),(1,3)]
## n,m=4,4
## edges=[(1,2),(2,3),(3,1),(1,4)]
## n,m=3,1
## edges=[(1,3)]
result=solution.networkRoads(solution,n,m,edges)
print(result)
总结
这道题目,解题的技巧就在于方法二。当我们一个点一个点的来进行遍历时时间复杂度很高,于是我们就直接遍历中间的路径。对于c++之类的,用方法一可以通过,但是对于python代码来说,就一定要采用花费时间最少的算法。