C/C++百题打卡[8/100]——七段码[不用dfs的题解、蓝桥杯2020省赛]




Sets ☁️

上一题链接: C/C++百题打卡[7/100]——【模板】并查集[洛谷].
百题打卡总目录: 🚧 🚧 …


一、题目总述

在这里插入图片描述



题目难度:⭐️⭐️⭐️⭐️⭐️


二、题目解析

● 如果认真读了题。题目的意思也不难理解。

● 关键是我们要用什么样的 数据结构 来存储题目中所说的 “二极管之间的连接情况”?以及用什么 算法 来解决 “是否符合题目的发光要求”?

● 这里,我用的 矩阵 来存储题目中所说的 “二极管之间的连接情况”。用 并查集 来解决 “是否符合题目的发光要求”。


● 我们假设每一个 发光二极管 对应 一个数字,那 7 个二极管之间的连接情况如下:

int Links[7][7]; 
// 假设规定:a=0, b=1, c=2, d=3, e=4, f=5, g=6
Links[0][1] = Links[0][5] = 1;  // ab, af
Links[1][0] = Links[1][2] = Links[1][6] = 1;  // ba, bc, bg
Links[2][1] = Links[2][3] = Links[2][6] = 1;  // cb, cd, cg
Links[3][2] = Links[3][4] = 1;  // dc, de
Links[4][3] = Links[4][5] = Links[4][6] = 1;  // ed, ef, eg
Links[5][0] = Links[5][4] = Links[5][6] = 1;  // fa, fe, fg
Links[6][1] = Links[6][2] = Links[6][4] = Links[6][5] = 1;  // gb, gc, ge, gf

● 每个二极管亮或不亮,我们用 Light[] 来表示,Light[0]=1 表示 a灯 亮,Light[3]=0 表示 d灯 不亮

● 如何输出 所有二极管的发光情况 呢?这里我也想了挺久的,网上几乎都是用的 dfs,但是我想到另外一个巧妙的方法,如下:

for (int i = 1; i <= 127; i++)
{
    int tmp = i;
    int ind = 0;
    while (ind < 7)
    {
        Light[ind] = tmp % 2;
        tmp = tmp >> 1;   	// 或者 tmp = tmp / 2;
        ind++;
    }
    for (int i = 0; i < 7; i++) // 初始化(用于并查集)
    	cout << Light[i] << " ";
	cout << " ← 对应的数字是:" << i <<  endl;
}

输出结果为:【用 7 位二进制数来表示 所有二极管的发光情况,则总共有 127 种发光情况,但是其中有一些不符合 “发光要求”,我们后续要用 并查集 进行处理】

在这里插入图片描述

● 关于 并查集,可参考上一期的题目 C/C++百题打卡[7/100]——【模板】并查集[洛谷]。这里我就不赘述算法细节了,详见下面的注释。



三、完整代码

#include <iostream>
using namespace std;
int f[7];
int Links[7][7];
int ans;
int Light[7];
int Find(int x)		// 并查集
{
    if (x == f[x])
        return x;
    return f[x] = Find(f[x]);
}
int main()
{
    // 假设规定:a=0, b=1, c=2, d=3, e=4, f=5, g=6
    Links[0][1] = Links[0][5] = 1;  // ab, af
    Links[1][0] = Links[1][2] = Links[1][6] = 1;  // ba, bc, bg
    Links[2][1] = Links[2][3] = Links[2][6] = 1;  // cb, cd, cg
    Links[3][2] = Links[3][4] = 1;  // dc, de
    Links[4][3] = Links[4][5] = Links[4][6] = 1;  // ed, ef, eg
    Links[5][0] = Links[5][4] = Links[5][6] = 1;  // fa, fe, fg
    Links[6][1] = Links[6][2] = Links[6][4] = Links[6][5] = 1;  // gb, gc, ge, gf

    for (int i = 1; i <= 127; i++)
    {
        int tmp = i;
        int ind = 0;
        while (ind < 7)
        {
            Light[ind] = tmp % 2;
            tmp = tmp >> 1;   // 或者 tmp = tmp / 2;
            ind++;
        }

        for (int i = 0; i < 7; i++) // 初始化(用于并查集)
            f[i] = i;

        for (int j = 0; j < 7; j++)	// 遍历整个 Links[7][7]
        {
            if (Light[j] == 1)		// 如果 j灯 亮, 再去看 Links[j][0]、Links[j][1]、...、Links[j][6]
            {
                for (int k = 0; k < 7; k++)
                {
                    if (Links[j][k] == 1 && Light[k] == 1) // "j灯" 与 "k灯" 之间有连接, 且 "k灯" 如果也亮的话
                    {
                        int f_i = Find(j);  // 找 "i灯" 的祖先节点(并查集)
                        int f_j = Find(k);  // 找 "j灯" 的祖先节点(并查集)
                        if (f_i != f_j)
                        {
                            f[f_i] = f_j;     // 合并为统一集合
                        }
                    }
                }
            }
        }

        int Father;
        for (int i = 0; i < 7; i++)
        {
            if (Light[i] == 1)
            {
                Father = Find(i);   // 随便找到一个集合的祖先结点
                break;
            }
        }
        
        int flag = 1;
        for (int j = 0; j < 7; j++)
        {
            if (Light[j] == 1 && Father != Find(j)) // 如果某个 灯j 亮, 但它的祖先结点不是 Father
            {
                flag = 0;   						// 则说明这些 发光的二极管 没有连成一片
                break;
            }
        }

        if (flag == 1)	// 所有发光的二极管的祖先结点都是一样的, 说明大家都连成了一片, 符合发光要求
            ans++;
    }
    cout << ans << endl;
    return 0;
}
// 答案是 80


四、做题小结与感慨

● 感觉用 7位二进制 来表示 “所有二极管的发光情况” 这个想法很巧妙,但也不是我一时半会想出来的,可能需要多练多积累吧。



五、参考附录

[1] 原题地址:https://www.lanqiao.cn/problems/595/learning/.

[2] 对于“并查集”讲得很不错的一篇知乎:《算法学习笔记(1) : 并查集》.

上一题链接: C/C++百题打卡[7/100]——【模板】并查集[洛谷].

百题打卡总目录: 🚧 🚧 …


[C/C++百题打卡[8/100]——七段码[蓝桥杯2020省赛]
标签:并查集

不定期更新     
   2022/3/16     

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一支王同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值