【蓝桥杯】 C++ 七段码 DFS 并查集⭐⭐⭐

文章讲述了如何使用七段码数码管来表示特殊文字,重点在于确保发光的二极管连成一片。通过建立邻接矩阵表示二极管间的连接,并使用并查集判断连通性,结合深度优先搜索(DFS)算法计算能表达的不同字符数量。解题过程涉及状态切换和根节点判断,文章提供了解题思路和注意事项,并引用了相关参考资料。
摘要由CSDN通过智能技术生成

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

小蓝要用七段码数码管来表示一种特殊的文字。
在这里插入图片描述
上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二 极管,分别标记为 a,b,c,d,e,f,g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符 的表达时,要求所有发光的二极管是连成一片的。

例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如 c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上 一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a,b,c,d,e 发光,f,g 不发光可以用来表达一种字符。
例如:b,f 发光,其他二极管不发光则不能用来表达一种字符,因为发光 的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?

实现代码

#include<bits/stdc++.h>
using namespace std;

// a b c d e f g
// 1 2 3 4 5 6 7
// 1 - 2 6
// 2 - 1 7 3
// 3 - 4 7 2
// 4 - 3 5
// 5 - 4 7 6
// 6 - 1 7 5
// 7 - 2 3 5 6

// 用邻接矩阵表示两个端点之间的灯管是不是连着
int g[7][7] = {
	{0, 1, 0, 0, 0, 1, 0},
	{1, 0, 1, 0, 0, 0, 1},
	{0, 1, 0, 1, 0, 0, 1},
	{0, 0, 1, 0, 1, 0, 0},
	{0, 0, 0, 1, 0, 1, 1},
	{1, 0, 0, 0, 1, 0, 1},
	{0, 1, 1, 0, 1, 1, 0}
};

int used[7]={0}; // 表示每个二极管是否有用过
int ans=0;      // 表示最后结果
int p[7];       // 存储这个点的 parent

int find(int x) // 查询该节点的根节点
{
    if(p[x]==x) // 如果该点的根节点是自己
    {
        return p[x]; // 返回自己的编号
    }

    else
    {
        p[x]=find(p[x]);    //父节点设为根节点
        return p[x];        //返回父节点
        // 如果它不是根节点 ,一层一层访问父节点,直至根节点(根节点的标志就是父节点是本身)
        // 要判断两个元素是否属于同一个集合,只需要看它们的根节点是否相同即可
        // 本代码已经实现路径优化
    }
}

void merge(int i,int j) // 把 i 和 j 合并到一个根节点下
{
    if(p[find(i)]!=p[find(j)])
    {
        p[find(i)]=p[j]; //  i 找出来的的根节点设为 j 的根节点
    }    
}

void dfs(int a) // a 表示已经遍历了 a 个二极管
{
    if(a==7) 
    {
        // 用并查集的思路去判断选择的这些发光二极管是不是连成一片(在同一个集合中)
        // 如果确实连成一片,就计数 1 次

        for(int i=0;i<7;i++)
        {
            p[i]=i; // 初始化,根节点是它自己
        }

        for(int i=0;i<7;i++)
        {
            for(int j=0;j<7;j++)
            {
                //cout<<"----test-----"<<i<<"---"<<j<<endl;
                if(g[i][j]==1&&used[i]==1&&used[j]==1)
                {
                    //cout<<i<<"---"<<j<<endl;
                    merge(i,j); // 如果 i 和 j 连着,同时 i 亮,j 也亮,把它们的根节点设为一个
                }
            }
        }

        int root_num=0; // 所有亮着的节点的根节点数
        for(int i=0;i<7;i++)
        {
            // 判断亮的这些是不是在一个集合
            // 比如 左边两根亮,右边两根也亮,但是它们不连着,也就是有两个根节点,不符合题目要求
            // 排除这种情况
            if(p[i]==i&&used[i])
            {
                root_num++; // 遍历所有亮着的二极管,如果它的根节点是自己,根结点数+1
            }
        }

        if(root_num==1) //如果只有一个根节点
        {
            ans++;
        }
        return;
    }

    used[a]=1; // 把这个二极管点亮,看这种情况下的树
    dfs(a+1);
    used[a]=0; // 把这个二极管熄灭,看这种情况下的树
    dfs(a+1);
}

int main()
{
    dfs(0); // 从 0 开始(0-6)
    cout<<ans;
    return 0;
}

解题思路

这个题对于我来说还是有点超前了……很多地方都不懂,改来改去现在才对。

  1. 首先这个题可以用邻接矩阵做,为什么呢?因为如果把每个二极管都看作一个节点,就可以画一个无向图,根据数据结构我们学过的知识(已经忘记的知识),可以通过邻接矩阵来存储每两个二极管之间的连通状态。
    在这里插入图片描述
  2. 确定用邻接矩阵之后,就要写一下节点合并和查询根节点的函数,在后面贴的文章里有具体解析:算法学习笔记(1) : 并查集
  3. dfs 函数比较难想,首先是状态,先打开本次的这个二极管,让它的下一个再进行 dfs 查询,查询结束后,再关掉本次的二极管,让它的下一个进行 dfs 查询。if 判断语句是看循环了几个二极管了,如果所有的二极管都进行了打开或关闭操作,就判断这些亮着的二极管是不是连着,再判断它们的根节点是不是一个。
    因为有时候会出现这样的状况:左边的两个亮,右边的两个也亮,在判断它们是否连着的时候,给出的结果是连着,但是实际上它们并不连着。这时候就要在前面把每个连着的区域的根节点统一,后面看有几个根节点。
  4. main 函数中 dfs 要从 0 开始,从 0 到 6 ,因为在存的时候,下标是从 0 开始存的。

注意点

  • 需要注意本题的 merge 函数,里面要判断两个节点的根节点是否为同一个点。

知识点

参考文章

这个文章是用暴力求解的:2020年蓝桥杯七段码,虽然很暴力,但是如果实在想不到算法这个很好理解,也很好实现,需要注意它后面列了三种特殊情况,这个有点难想。

这两个文章全用的是并查集+dfs来解决:第十一届蓝桥杯 ——七段码七段码(2020年蓝桥杯省赛)

同时发现了宝藏博主的文章,对历年题很有研究,而且题解很简洁易懂:蓝桥杯省赛 C/C++ ABC组题解(第四届 ~ 第十二届)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值