【蓝桥杯基础题】2017年省赛—九宫幻方

简介: 【蓝桥杯基础题】2017年省赛—九宫幻方

一、题目背景

本题为2020年省赛程序设计题

  • C/C++ C 组第8题

二、题目描述

1.问题描述

小明最近在教邻居家的小朋友小学奥数,而最近正好讲述到了三阶幻方这个部分,三阶幻方指的是将1~9不重复的填入一个3 x 3的矩阵当中,使得每一行、每一列和每一条对角线的和都是相同的。
三阶幻方又被称作九宫格,在小学奥数里有一句非常有名的口诀:“二四为肩,六八为足,左三右七,戴九履一,五居其中”,通过这样的一句口诀就能够非常完美的构造出一个九宫格来。

4 9 2
3 5 7
8 1 6

有意思的是,所有的三阶幻方,都可以通过这样一个九宫格进行若干镜像和旋转操作之后得到。现在小明准备将一个三阶幻方(不一定是上图中的那个)中的一些数抹掉,交给邻居家的小朋友来进行还原,并且希望她能够判断出究竟是不是只有一个解。而你,也被小明交付了同样的任务,但是不同的是,你需要写一个程序。

2.输入格式  

输入仅包含单组测试数据。
每组测试数据为一个3 x 3的矩阵,其中为0的部分表示被小明抹去的部分。
对于100%的数据,满足给出的矩阵至少能还原出一组可行的三阶幻方。

3.输出格式  

如果仅能还原出一组可行的三阶幻方,则将其输出,否则输出“Too Many”(不包含引号)。

4.一个例子

下面是一个样例

输入:

0 7 2
0 5 0
0 3 0

输出:

6 7 2
1 5 9
8 3 4

三、题目分析

1. 执果索因

通过使用科技的力量或者自己独立思考,提前找出三阶幻方的所有可能解。
再将自己已经知道的所有解提前放入程序里面,与输入的数进行比对。
如果只能比对出一组可行的幻方,则将其输出。
科技力量直通车→科技创造美好生活
在这里插入图片描述

2. 全排列

上面的方法是将已知答案放入程序中,但是更多的情况,我们是不知道答案的。

对此,我们只能将所有可能都弄出来。

这里就需要利用数学中的排列知识了。

排列,一般地,从n个不同元素中取出m(m≤n)个元素,按照一定的顺序排成一列,叫做从n个元素中取出m个元素的一个排列(permutation)。特别地,当m=n时,这个排列被称作全排列(all permutation)。

C++algorithm中提供了内置的全排列函数next_permutation

下面介绍一下next_permutation函数

在这里插入图片描述

使用方法:next_permutation(数组头地址,数组尾地址);

若下一个排列存在,则返回真,如果不存在则返回假

举个例子:

#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
    int b[3] = { 1,2,3 };
    do
    {
        for (int i = 0; i < 3; ++i)
            cout << b[i] ;
        cout << endl;
    } while (next_permutation(b, b + 3));
    return 0;
}

在这里插入图片描述

利用循环,和next_permutation的返回值,巧妙的得到全排列结果。
然后利用九宫幻方的性质,检查是不是九宫幻方。

九宫幻方:每一行、每一列、每一对角线的和都相同。

3. 深度优先搜索(DFS)

注意:完全不知道DFS是什么的可以看下这篇博客:深度优先搜索(DFS)

在这里插入图片描述

四、代码汇总

1. 执果索因

#include <iostream>
#include<string>
#include <algorithm>
using namespace std;
int main()
{
    int a[3][3], f = 0, k = 0, i, j;
    //初始化
    string b = "000000000";
    //所有可能的答案
    string vis[8] = { "816357492","618753294","492357816","294753618","672159834","834159672","276951438","438951276" };
    int v[8] = { 0 };
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            cin >> a[i][j];
            //将int转为string
            b[k++] = a[i][j] + '0';
        }
    }
    for (i = 0; i < 8; i++)
    {
        for (j = 0; j < 9; j++)
        {
            if (b[j] != '0')
            {
                //比较下一个字符串
                if (vis[i][j] != b[j])
                    break;
            }
        }
        if (j == 9)
        {
            f++;
            v[i] = 1;
        }
        if (f > 1)
        {
            cout << "Too Many" << endl;
            break;
        }
    }
    k = 0;
    if (f == 1)
    {
        for (i = 0; i < 9; i++)
            if (v[i] == 1)break;
        while (k < 9)
        {
            cout << vis[i][k++] << " ";
            if (k % 3 == 0)cout << endl;
        }
    }
    return 0;
}

2. 全排列

#include <iostream>
#include<string>
#include <algorithm>
using namespace std;
int arr[9];
int str[9] = { 1,2,3,4,5,6,7,8,9 };

bool flag = true;
int ans = 0;
//检查是不是九宫幻方
bool check(int a[])
{
    int a1 = a[0] + a[1] + a[2];
    int a2 = a[3] + a[4] + a[5];
    int a3 = a[6] + a[7] + a[8];
    int a4 = a[0] + a[3] + a[6];
    int a5 = a[1] + a[4] + a[7];
    int a6 = a[2] + a[5] + a[8];
    int a7 = a[2] + a[4] + a[6];
    int a8 = a[0] + a[4] + a[8];
    if (a1 == a2 && a2 == a3 && a3 == a4 && a4 == a5 && a5 == a6 && a6 == a7 && a7 == a8)
        return true;
    return false;
}

int main() 
{
    for (int i = 0; i < 9; i++)
        cin >> arr[i];
    do {
        if (check(str))
        {
            flag = true;
            for (int i = 0; i < 9; i++)
            {
                if (arr[i] != 0 && arr[i] != str[i])
                {
                    flag = false;
                }
            }

            if (flag)
            {
                for (int i = 0; i < 9; i++)
                    arr[i] = str[i];
                ans++;
            }
        }
    } while (next_permutation(str, str + 9));

    if (ans == 1)
    {
        int k = 0;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                cout << arr[k] << " ";
                k++;
            }
            cout << endl;
        }
    }
    else if (ans > 1)
        cout << "Too Many" << endl;
    return 0;
}

3. DFS

#include <iostream>
#include<string>
#include <algorithm>
int a[5][5];
int b[5][5];
int vis[15];
int ans = 0;
using namespace std;
bool check()
{
    int sum1, sum2;
    int x = a[1][1] + a[1][2] + a[1][3];
    for (int i = 1; i <= 3; i++)
    {
        sum1 = 0, sum2 = 0;
        for (int j = 1; j <= 3; j++)
        {
            sum1 += a[i][j];
            sum2 += a[j][i];
        }
        if (sum1 != x || sum2 != x)
            return 0;
    }
    sum1 = a[1][1] + a[2][2] + a[3][3];
    sum2 = a[1][3] + a[2][2] + a[3][1];
    if (sum1 != x || sum2 != x)
        return false;
    return true;
}
void dfs(int num)
{
    if (num == 9)
    {
        if (check())
        {
            for (int i = 1; i <= 3; i++)
                for (int j = 1; j <= 3; j++)
                    b[i][j] = a[i][j];
            ans++;
        }
        return;         
    }
    for (int i = 1; i <= 3; i++)
        for (int j = 1; j <= 3; j++)
        {
            if (a[i][j] == 0)
            {
                for (int k = 1; k <= 9; k++)
                {
                    if (!vis[k])
                    {
                        vis[k] = 1;
                        a[i][j] = k;
                        dfs(num + 1);
                        vis[k] = 0;
                        a[i][j] = 0;
                    }
                }
                return;
            }
        }
}
int main()
{
    int num = 0;
    for (int i = 1; i <= 3; i++)
        for (int j = 1; j <= 3; j++)
        {
            cin >> a[i][j];
            vis[a[i][j]] = 1;
            if (a[i][j] != 0)
                num++;
        }
    dfs(num);
    if (ans == 1)
        for (int i = 1; i <= 3; i++)
        {
            for (int j = 1; j <= 3; j++)
                cout << b[i][j] << " ";
            cout << endl;
        }
    else
        cout << "Too Many" << endl;
    return 0;
}

五、总结

1. 全排列

排列、全排列的计算。

全排列函数:next_permutation 的用法。

2. 九宫幻方的判断

九宫幻方:每一行、每一列、每一对角线的和都相同。

相关文章
|
Windows 并行计算
蓝桥杯 历届试题 九宫重排
问题描述   如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。  我们把第一个图的局面记为:12345678.  把第二个图的局面记为:123.46758  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
834 0
|
3月前
|
人工智能 算法 Java
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1005 数字游戏
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1005 数字游戏
57 0
|
3月前
|
Java C语言 C++
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1000 kAc给糖果你吃
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-1000 kAc给糖果你吃
39 0
|
3月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-999 数的潜能
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-999 数的潜能
38 0
|
3月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-997 粘木棍
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-997 粘木棍
48 0
|
3月前
|
机器学习/深度学习 算法 Java
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-996 车的放置
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-996 车的放置
42 0
|
3月前
|
人工智能 算法 Java
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-992 士兵杀敌(二)
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-992 士兵杀敌(二)
27 1
|
3月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-986 藏匿的刺客
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-986 藏匿的刺客
43 0
|
3月前
|
算法 Java C语言
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-985 幸运的店家
第十四届蓝桥杯集训——练习解题阶段(无序阶段)-ALGO-985 幸运的店家
46 0