蓝桥杯七段码(递归、并查集连通性判断)

118 篇文章 22 订阅
9 篇文章 0 订阅

1. 问题描述:

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。小蓝要用七段码数码管来表示一种特殊的文字。

上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。
这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

来源:https://www.lanqiao.cn/problems/595/learning/

2. 思路分析:

① 分析题目可以知道如果没有题目中所有亮的灯都要连在一起的条件那么这道题目就很简单了,因为每个灯存在两种状态:亮与不亮,所以灯的情况总共有2^7=128种,所以当存在这个限制条件的时候就需要排除一些不符合题目的情况。因为存在亮与不亮两种状态所以可以使用递归解决,当七盏灯的情况确定之后那么就需要判断亮的这些灯是否连在一起。判断是否连在一起的特点可以知道这是一个连通性的判断问题,对于连通性判断主要有两种方法,第一种方法是使用dfs进行搜索判断,将所有存在联系的节点都连接在一起,第二种是使用并查集的方法进行判断,下面使用了并查集的方法进行连通性的判断

② 使用并查集判断连通性其实理解之后也还是比较简单,主要涉及到并查集的两个操作(查找与合并操作),查找当前节点x的父节点与合并当前x, y的父子关系(当x节点的父节点不等于y节点的父节点的时候进行合并),查找当前节点x的父节点可以使用递归的方法查找,并且在查找当前节点的父节点的时候对根节点往下连接的节点进行统一的联系,通过下面的图就可以很清楚理解了,主要是将所有具有联系节点的父节点统一指向一个父节点,这样就可以查找出最终的那个父节点。

       

并查集的查找与合并操作,合并前需要先查找当前两个节点x,y的父节点,当father[x] != father[y]的时候进行合并,下面是具体的代码,其实理解之后记住即可(一般都是这样的操作)

def union(self, x: int, y: int, father: List[int]):
    fa = self.find(x, father)
    fb = self.find(y, father)
    if fa != fb:
        father[fa] = fb
        return

def find(self, x: int, father: List[int]):
    if x == father[x]: return x
    father[x] = self.find(father[x], father)
    return father[x]

并查集其实有两种写法,不同的点主要是根据一开始的时候初始化father数组的值的不同,第一种写法是初始化father数组为本身位置的值,也即father[i] = i。第二种写法是一开始的时候初始化所有的father数组值全为0。其中第一种初始化father数组是常规的写法,感觉第二种写法会更加简单一点。下面举一个蓝桥杯合根植物的例子来说明一下,一开始的时候初始化father数组全为0,下面的例子可以声明一个长度为20的数组,我们其实在查找的时候输入(1,5),(5,9),(9,10),(10,12),(12,16)...左边的联通块的时候,实际上是修改了1,5,6,10,11,12,16,18的父节点,最后17这个节点的父节点没有修改也就是0,所以可以知道只要是具有联系的两个节点的父节点都是被修改了的,直到到达最后的那个节点,因为最后那个节点没有与其他的节点进行连续了所有父节点是为0的,所以我们最终只需要判断出father数组中节点为0的个数即可知道连通块的数目,使用这个方法进行判断是非常快速而且简单的,只需要判断一开始的时候声明全为0的father数组,然后查找所有具有关系的父节点,最终通过计算father中为0的数目就为连通块的数目。

第二种方法其实也是类似的,因为father数组初始化的时候为father[i] = i,所以在查找父节点的时候也是修改了具有联系节点的父节点,只有最终的那个父节点是没有被修改的,最后那个父节点就为本身,所以我们只需要判断出父节点为本身的节点的数目即为连通块的数目

③ 这道题目其实一个很妙的点是利用了二维列表的下标关系来表示灯的连通情况,我们就可以通过最终亮的灯来判断灯是否连接在一起

# 字母a的连通情况
graph[0][1] = graph[0][5] = 1
# 字母b的连通情况
graph[1][0] = graph[1][2] = graph[1][6] = 1
# 字母c的连通情况
graph[2][1] = graph[2][6] = graph[2][3] = 1
# 字母d的连通情况
graph[3][2] = graph[3][4] = 1
# 字母e的连通情况
graph[4][5] = graph[4][3] = graph[4][6] = 1
# 字母f的连通情况
graph[5][0] = graph[5][4] = graph[5][6] = 1
# 字母g的连通情况
graph[6][5] = graph[6][1] = graph[6][2] = graph[6][4] = 1

两种不同初始化father数组的方法在查找的时候也略微有点不同:

3. 代码如下:

from typing import List
import collections


class Solution:
    res = 0
    
    def solve(self, lights: List[int], index: int, graph: List[List[int]]):
        self.dfs(lights, 0, graph)
        return self.res
    
    # 合并x, y节点
    def union(self, x: int, y: int, father: List[int]):
        fa = self.find(x, father)
        fb = self.find(y, father)
        if fa != fb:
            father[fa] = fb
            return
    # 递归查询x节点的父节点
    def find(self, x: int, father: List[int]):
        if x == father[x]: return x
        father[x] = self.find(father[x], father)
        return father[x]

    def dfs(self, lights: List[int], index: int, graph: List[List[int]]):
        if index == len(lights):
            father = [i for i in range(7)]
            t = list()
            # 使用一个列表来记录所有亮的灯的下标这样才可以判断灯的连通情况, 
            # 后面通过才可以通过所有亮的情况进行连通
            for i in range(7):
                if lights[i]: t.append(i)
            for i in range(len(t)):
                for j in range(i + 1, len(t)):
                    # t[i]与t[j]两个亮的灯有连通关系那么就合并起来
                    if graph[t[i]][t[j]] == 1:
                        self.union(t[i], t[j], father)
            count = 0
            for i in range(len(t)):
                # 判断当前的节点是否是自己如果是自己那么表示当前的节点就是最终的父节点
                if father[t[i]] == t[i]: count += 1
            s = ""
            # 输出灯亮的情况对应的字符串
            for i in range(len(lights)):
                if lights[i] == 1:
                    s += chr(i + ord("a"))
            print(s)
            # 判断是否只有一个连通块
            if count == 1:
                self.res += 1
            return
        # 当前的灯为亮
        lights[index] = 1
        self.dfs(lights, index + 1, graph)
        # 回溯, 令当前灯为不亮
        lights[index] = 0
        self.dfs(lights, index + 1, graph)


if __name__ == '__main__':
    graph = [[0] * 7 for i in range(7)]
    graph[0][1] = graph[0][5] = 1
    graph[1][0] = graph[1][2] = graph[1][6] = 1
    graph[2][1] = graph[2][6] = graph[2][3] = 1
    graph[3][2] = graph[3][4] = 1
    graph[4][5] = graph[4][3] = graph[4][6] = 1
    graph[5][0] = graph[5][4] = graph[5][6] = 1
    graph[6][5] = graph[6][1] = graph[6][2] = graph[6][4] = 1
    lights = [0] * 7
    print(Solution().solve(lights, 0, graph))
from typing import List
import collections

class Solution:
    res = 0
    def solve(self, lights: List[int], index: int, graph: List[List[int]]):
        self.dfs(lights, 0, graph)
        return self.res

    def union(self, x: int, y: int, father: List[int]):
        fa = self.find(x, father)
        fb = self.find(y, father)
        if fa != fb:
            father[fa] = fb
            return

    def find(self, x: int, father: List[int]):
        # 这里判断的是当前节点的父节点是否为0, 因为在一开始的时候就声明了father数组元素全为0
        if father[x] == 0: return x
        father[x] = self.find(father[x], father)
        return father[x]

    def dfs(self, lights: List[int], index: int, graph: List[List[int]]):
        # 说明七盏灯的情况已经确定好了
        if index == len(lights):
            father = [0] * 7
            t = list()
            for i in range(7):
                if lights[i]: t.append(i)
            for i in range(len(t)):
                for j in range(i + 1, len(t)):
                    if graph[t[i]][t[j]] == 1:
                        self.union(t[i], t[j], father)
            # print(father)
            count = 0
            for i in range(len(t)):
                if father[t[i]] == 0: count += 1
            s = ""
            for i in range(len(lights)):
                if lights[i] == 1:
                    s += chr(i + ord("a"))
            if count == 1:
                self.res += 1
            return
        lights[index] = 1
        self.dfs(lights, index + 1, graph)
        lights[index] = 0
        self.dfs(lights, index + 1, graph)



if __name__ == '__main__':
    graph = [[0] * 7 for i in range(7)]
    graph[0][1] = graph[0][5] = 1
    graph[1][0] = graph[1][2] = graph[1][6] = 1
    graph[2][1] = graph[2][6] = graph[2][3] = 1
    graph[3][2] = graph[3][4] = 1
    graph[4][5] = graph[4][3] = graph[4][6] = 1
    graph[5][0] = graph[5][4] = graph[5][6] = 1
    graph[6][5] = graph[6][1] = graph[6][2] = graph[6][4] = 1
    lights = [0] * 7
    print(Solution().solve(lights, 0, graph))

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值