【蓝桥杯】危险系数Python满分解法

【蓝桥杯】危险系数Python满分解法


前言

小白一枚,花了一天的时间整了蓝桥杯的危险系数方法,在此记录一下解法~~

一、方法一

首先,我一开始使用的方法是进行树的深度优先搜索,将所有的从u节点到v节点的所有路径集合,然后看看那些路径在路径集合中都出现。记录一下这些反复出现的路径所对应的节点。最后将节点进行输出
当然,这种算法时间复杂度为n!,对于Python代码来说超时了。。。

在这里插入图片描述

class Solution:
    #该题目用来求有n个节点,m条通道的,以edges构造的地道树中,节点u和节点v之间的危险系数


    def dangerCoefficient(self,n,m,edges,u,v):
        def dsf(val,v):
            if val ==v:
                res.append(path.copy())
            for i in range(n):
                if not used[i] and (edges[i][0]==val or edges[i][1]==val):
                    used[i]=True
                    path.append(edges[i])
                    if edges[i][0]==val:
                       
                        dsf(edges[i][1],v)
                    else:
                        dsf(edges[i][0],v)
                    path.pop()
                    used[i]=False
                    
        edges.sort(key=lambda x:x[0])
        used=[False for _ in range(m)]
        res=list()
        path=list()
        ans=0
        n=len(edges)
        dsf(u,v)
        singlar_edges=list()
        nodes=set()
        for temp in res[0]:

            if all(temp in res_part for res_part in res):

                if temp[0] not in nodes and temp[0]!=u and temp[0]!=v:
                    nodes.add(temp[0])
                if temp[1] not in nodes and temp[1]!=u and temp[1]!=v:
                    nodes.add(temp[1])
        ans=len(nodes)      
        return ans
                
if __name__ == "__main__":
    solution = Solution
    n,m=map(int,input().split())
    edges=[]
    for i in range(m):
        temp1,temp2=map(int,input().split())
        edges.append((temp1,temp2))
    u,v=map(int,input().split())
##    n,m=7,6
##    edges=[(1,3),(2,3),(3,4),(3,5),(4,5),(5,6)]
##    u,v=1,6
##    n,m=5,4
##    edges=[(1,3),(2,3),(1,4),(4,5)]
##    u,v=2,5
    result = solution.dangerCoefficient(solution,n,m,edges,u,v)
    print(result)

二、方法二:满分解法

在这里插入图片描述

在第一种情况拿不到满分的情况下我们不得不想一下别的解法: 于是我们采用 割点 的思想。
首先,整体思路:

1.确定结构

本题目我们的边都是无向边,即(1,3)既表示从节点1可以到达节点3,又表示节点3可以到达节点1。对于这个题目,我们将一条有向边等价成两条无向边,即节点1到节点3的边和节点3到节点1的边。

2.定义两点之间的边这种类,edge

该类中包含两个属性,to_node表示这条边的终点节点的编号。pre_edge表示这条边的源点除了这条边本身的其余出边
可能读者不太理解这句话什么意思,没关系,看看后面的再重新读一遍这个部分就会茅塞顿开

3.根据输入构造图结构

比如我们输入的边数组为 [(1,3),(2,3),(3,4),(3,5),(4,5),(5,6)],则具体的节点和边的拓扑结构如下:
在这里插入图片描述
接下来我们输出一下各个边的对象看一下
在这里插入图片描述
好的重点来了!
我们是按照边的顺序,从0到m(不包含m)来进行每一条边的构建的。
构建编号为0的边,终点为3,其源点1没有额外的出边
构建编号为1的边,终点为1,其源点3没有额外的出边
构建编号为2的边,终点为3,其源点2没有额外的出边
构建编号为3的边,终点为2**,其源点3有一条之前的出边,编号为1**


通过如此构建,我们可以将输入的边按照某一种结构进行记录下来。我们就是通过这种结构再结合深度优先搜索不断地找寻所有的通路

4 进行割点

在进行割点前,我们先判断u和v是否联通。如果不联通,直接返回-1。如果连通,我们通过now标记当前被删除的节点。通过一个一个的遍历讨论节点,查看删除某个特定节点后原图是否联通,从而得到最终的解

5 代码

class Edge:
    #to表示该条边的终点节点的编号
    #pre_edge表示从该边源点出发的上一条边
    def __init__(self,to_node):
        self.to_node=to_node
        self.pre_edge=-1
class Solution:
    def dangerCoefficient(self,n,m,edges,u,v):
        #u表示源点,v表示终点
        def add_edge(u,v):
            nonlocal cnt
            edge[cnt].to=v
            edge[cnt].pre_edge=pre[u]
            pre[u]=cnt
            cnt+=1
        def dsf(s):
            if s==ed:
                return 1
            flag[s]=True
            p=pre[s]
            while p!=-1:
                temp=edge[p].to
                p=edge[p].pre_edge
                if flag[temp]==True or temp==now:
                    continue
                if dsf(temp)==1:
                    flag[s]=False
                    return 1
            flag[s]=False
            return 0
                
                
            
        flag=[False for _ in range(n)]
        edge=list()
        cnt=0
        pre=[-1 for _ in range(n+1)]
        for x,y in edges:
            edge.append(Edge(y))
            add_edge(x,y)
            edge.append(Edge(x))
            add_edge(y,x)

##        for i in range(len(edge)):
##            print('边的编号:',i)
##            print('边的终点to:',edge[i].to_node)
##            print('边的源点的另外一条出边:',edge[i].pre_edge)
##            print('===================')
        ed=v
        now=-1

        if dsf(u)==0:

            return -1
        else:
            ans=0
            for i in range(n):
                if i==u or i==v:
                    continue
                now=i
                if dsf(u)==0:
                    ans+=1
            return ans
        return 0
        


    
if __name__ == "__main__":
    solution = Solution
    n,m=map(int,input().split())
    edges=[]
    for i in range(m):
        temp1,temp2=map(int,input().split())
        edges.append((temp1,temp2))
    u,v=map(int,input().split())
##    n,m=7,6
##    edges=[(1,3),(2,3),(3,4),(3,5),(4,5),(5,6)]
##    u,v=1,6
##    n,m=5,4
##    edges=[(1,3),(2,3),(1,4),(4,5)]
##    u,v=2,5
    result = solution.dangerCoefficient(solution,n,m,edges,u,v)
    print(result)

总结

通过这次算法,基本理解了割点的思想。对于Python代码来说,一般的深度优先搜索往往会超时。而第二种方法较第一种的优点就是之前通过O(n)的时间,通过类来将该图的结构特点进行了记录,从而避免了全部的深度优先,节省了时间

方法三

在这里插入图片描述

#第三种方案:
#思路是先通过遍历来保留树的结构
#然后割点
from time import time
class Solution:
    def dangerCoefficient(self,n,m,edges,u,v):
        #n表示我们讨论到第n个点
        def dsf(n):
            if n==v:
                return True
##            print('flag:',flag)
            flag[n]=True
##            for i in range(m):
            for i in node_edge[n]:
##                if not used[i] and now not in edges[i] and node_edge[n]>>i & 1==1 :
                if not used[i] and now not in edges[i]:
                    used[i]=True
                    if edges[i][0]==n and not flag[edges[i][1]] and dsf(edges[i][1]):
                        flag[n]=False
                        used[i]=False
                        return True
                    else:
                        if not flag[edges[i][0]] and dsf(edges[i][0]):
                            flag[n]=False
                            used[i]=False
                            return True
                    used[i]=False
            flag[n]=False
            return False    
        node_edge=[0 for _ in range(n+1)]
        cnt=0
##        node_edge[i]表示的是第i个节点的相邻边
##        node_edge=[[]for _ in range(n+1)]
        for x,y in edges:
            node_edge[x].append(cnt)
            node_edge[y].append(cnt)
            cnt+=1
##        for x,y in edges:
##            node_edge[x]|=1<<cnt
##            node_edge[y]|=1<<cnt
##            cnt+=1
##        print(node_edge)
        #used表示某一条边是否已经使用
        used=[False for _ in range(m)]
        #flag在此表示某一个结点是否使用了
        flag=[False for _ in range(n+1)]
        now=-1
        #先提前进行遍历看看通不通
        if not dsf(u):
            return -1
        ans=0
        for temp in range(1,n+1):
            if temp==u or temp ==v:
                continue
            now=temp
            if not dsf(u):
                ans+=1
        return ans
            
        
            
        
            
if __name__ == "__main__":
    solution = Solution
    n,m=map(int,input().split())
    edges=[]
    for i in range(m):
        temp1,temp2=map(int,input().split())
        edges.append((temp1,temp2))
    u,v=map(int,input().split())
##    n,m=7,6
##    edges=[(1,3),(2,3),(3,4),(3,5),(4,5),(5,6)]
##    u,v=1,6
##    n,m=5,4
##    edges=[(1,3),(2,3),(1,4),(4,5)]
##    u,v=2,5
##    pre=time()
    result = solution.dangerCoefficient(solution,n,m,edges,u,v)
##    print(time()-pre)
    print(result)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值