2020蓝桥杯省赛C/C++ 七段码 简单易懂的思路

2020蓝桥杯省赛C/C++ 七段码 简单易懂的思路

本文思路来源于:
2016蓝桥杯c/c++省赛7–剪邮票

此处有该题的视频讲解(顺便感谢一下算法启蒙大佬):
蓝桦枫玥–2016蓝桥杯c/c++省赛7–剪邮票

以下放该题的思路和代码, 强烈建议先看懂该题, 再分析七段码, 会该题的大佬可以直接跳过~


该题是对之前13年蓝桥杯题目剪格子的直接回应: 剪格子只考虑了 “L” 形 (只有两个端点) 的连通情况, 从一个端点搜到另一个端点符合条件就直接退出了。而忽略了 “T” 和 “十” 字形 (多个端点) , 而剪邮票使用的方法兼顾了这两种情况。

而该题是一个无向图的情况, 类似于剪邮票的多个端点情况, 所以完全可以搬过来。

具体思路为:

  1. 用一维数组和next_permutation去列出所有的情况, 然后映射到二维数组。
  2. 对二维数组进行检查, 只要遇到一个符合的块, 就去深搜所有的连通块,全部变成0, cnt++。
  3. 最后如果cnt=1说明只有一个连通块。

ps: 因为该题采取了从1置0的方法, 所以不需要传统dfs的visit数组去判断某一点是否搜索过

剪邮票完整代码
#include 
using namespace std;
#define MAX 12
// 该方法完美兼容"T"字形和"十"字形
int a[MAX] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1};
// 映射数组
int b[3][4] = {0};
// 搜索顺序: 上左右下
int u[4] = {-1, 0, 0, 1};
int v[4] = {0, -1, 1, 0};
// 将该点所有连通点全部置0
void dfs(int x, int y)
{
  b[x][y] = 0;
  // 按照上左右下的顺序进行搜索
  // 一个完整的过程为:将所有的区域置为0后, 退回开始点
  for (int i = 0; i < 4; i++)
  {
    int xx = x + u[i];
    int yy = y + v[i];
    // 若该点未越界并且还没有遍历
    if (xx >= 0 && xx < 3 && yy >= 0 && yy < 4 && b[xx][yy] == 1)
      dfs(xx, yy);
  }
}
bool check()
{
    // 数组映射
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 4; j++)
      b[i][j] = a[4 * i + j];
  int cnt = 0;
  // 当区域为连通块时, 搜索后数组全部置为0, 则后面的循环全部不通过if语句
  // 若cnt>1, 说明存在不止一个区域
  for (int i = 0; i < 3; i++)
    for (int j = 0; j < 4; j++)
    {
    // 搜索到符合条件的点
      if (b[i][j] == 1)
      {
        dfs(i, j);
        cnt++;
      }
    }
  if (cnt == 1)
    return true;
  return false;
}
int main()
{
  int ans = 0;
  do
  {
    if (check())
      ans++;
  } while (next_permutation(a, a + 12));
  cout << ans;
  return 0;
}

然后是七段码

小蓝要用七段码数码管来表示一种特殊的文字。

七段码

七段码上图给出了七段码数码管的一个图示,数码管中一共有7 段可以发光的二极管,分别标记为a, b, c, d, e, f, g。小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。
例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。
请问,小蓝可以用七段码数码管表达多少种不同的字符?


该题的思路跟上题大同小异, 只不过从一个二维的数组深搜变成了一个无向图, 而且不止连通5个。

为了方便理解, 我们与上题一样进行三步走。

1.初始化无向图, 用一维数组和next_permutation列出所有的情况, 然后映射到另一个一维数组

// 无向图数组
int a[MAX][MAX] = {0};
// 亮灯数组
int b[MAX] = {0};
// 映射数组
int c[MAX] = {0};
// 初始化图
void init()
{
    // 表示两个位置相邻
    a[0][1] = 1;
    a[0][5] = 1;
    a[1][2] = 1;
    a[1][6] = 1;
    a[2][3] = 1;
    a[2][6] = 1;
    a[3][4] = 1;
    a[4][5] = 1;
    a[4][6] = 1;
    a[5][6] = 1;
}

 // 可能亮1至7段
    for (int i = 0; i < MAX; i++)
    {
        // 初始化亮灯数组
        memset(b, 0, sizeof(b));
        // *注释1
        for (int j = 7; j >= i; j--)
        {
            b[j] = 1;
        }
        do
        {
            // 如果确实是一段
            if (check())
                sum++;
        } while (next_permutation(b, b + MAX));
    }

*注释1:
此处注意一个小问题: next_permutation的全排列中的1一定要从后面往前加, 如果初始化为{1,1,1,0,0,0,0}, 则全排列只有这一种情况, 必须初始化为{0,0,0,0,1,1,1}。


2.check函数检查该状态是否符合条件, 搜索一个连通点, 然后去深搜它的相邻点, 将它们全部变成0, 同时flag++, 若flag=1, 说明是单连通块, 否则就是多个连通块。

bool check()
{
    // 数组映射
    for (int i = 0; i < MAX; i++)
    {
        c[i] = b[i];
    }
    int flag = 0;
    for (int i = 0; i < MAX; i++)
    {
        // 如果i位置亮灯
        if (c[i])
        {
            // 把他周围的灯全部灭掉
            dfs(i);
            // 连通块数+1
            flag++;
        }
    }
    // 如果全连着, 也就是上述if语句只符合了一次
    if (flag == 1)
        return true;
    return false;
}

3.dfs函数进行深搜, 若搜索到i点与k点相连, 则递归进入i点。

// 深搜与k相邻的点并且关掉
void dfs(int k)
{
    // 把k位置关掉
    c[k] = 0;
    for (int i = 0; i < MAX; i++)
    {
        // 若i位置与k位置相连
        if (a[i][k] || a[k][i])
        {
            // 若i位置亮着灯
            if (c[i])
            {
                // 深搜
                dfs(i);
            }
        }
    }
}

最后放上完整代码

七段码完整代码
    #include 
    #define MAX 7
    using namespace std;
    // 相邻数组
    int a[MAX][MAX] = {0};
    // 亮灯数组
    int b[MAX] = {1, 1, 0, 1, 1, 1, 0};
    // 映射数组
    int c[MAX] = {0};
    // 初始化图
    void init()
    {
        a[0][1] = 1;
        a[0][5] = 1;
        a[1][2] = 1;
        a[1][6] = 1;
        a[2][3] = 1;
        a[2][6] = 1;
        a[3][4] = 1;
        a[4][5] = 1;
        a[4][6] = 1;
        a[5][6] = 1;
    }
    // 深搜相邻的点并且关掉
    void dfs(int k)
    {
        // 把k位置关掉
        c[k] = 0;
        for (int i = 0; i < MAX; i++)
        {
            // 若i位置与k位置相连
            if (a[i][k] || a[k][i])
            {
                // 如果i位置亮着灯
                if (c[i])
                {
                    // 深搜
                    dfs(i);
                }
            }
        }
    }
    bool check()
    {
        // 数组映射
        for (int i = 0; i < MAX; i++)
        {
            c[i] = b[i];
        }
        int flag = 0;
        for (int i = 0; i < MAX; i++)
        {
            // 如果i位置亮灯
            if (c[i])
            {
                // 把他周围的灯全部灭掉
                dfs(i);
                // 连通块数+1
                flag++;
            }
        }
        // 如果全连着
        if (flag == 1)
            return true;
        return false;
    }
    int main()
    {
        init();
        int sum = 0;
        // 可能亮1至7段
        for (int i = 0; i < MAX; i++)
        {
            // 初始化亮灯数组
            memset(b, 0, sizeof(b));
            for (int j = 7; j >= i; j--)
            {
                b[j] = 1;
            }
            do
            {
                // 如果确实是一段
                if (check())
                    sum++;
            } while (next_permutation(b, b + MAX));
        }
        cout << sum;//结果为80
    }

你可能感兴趣的:(算法,算法,dfs)