深搜——蓝桥杯之方格填数,寒假作业

问题描述:

方格填数


如下的10个格子
   +--+--+--+
   |  |  |  |
+--+--+--+--+
|  |  |  |  |
+--+--+--+--+
|  |  |  |
+--+--+--+


(如果显示有问题,也可以参看下图)


填入0~9的数字。要求:连续的两个数字不能相邻。
(左右、上下、对角都算相邻)


一共有多少种可能的填数方案?


请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

 

首先,不必花心思考虑怎么把这个图形在程序中表现出来,一个一维数组就可以搞定,我们这样做:

自上而下,从左向右分别给这些单元格标记为 a[1],a[2],...,a[10]。

要做到题述要求,对于 a[1],只需要 a[1] 和 a[2],a[4],a[5],a[6] 之间的绝对值大于 1 即可。对其他位置亦是如此,如果填写的不符合要求就回溯,和深搜的思想一样,如果不是很理解深搜,请看这篇文章:

http://blog.csdn.net/eliminatedacmer/article/details/79334903

我们只需要将符合题意的结果找出来就可以了:

#include <iostream>
#include <cmath>

using namespace std;

int count = 0;
int a[12],b[12];
void Check()
{
    if(abs(a[1]-a[2])>1 && abs(a[1]-a[4])>1 && abs(a[1]-a[5])>1 && abs(a[1]-a[6])>1 &&
       abs(a[2]-a[3])>1 && abs(a[2]-a[5])>1 && abs(a[2]-a[6])>1 && abs(a[2]-a[7])>1 &&
       abs(a[3]-a[6])>1 && abs(a[3]-a[7])>1 &&
       abs(a[4]-a[5])>1 && abs(a[4]-a[8])>1 && abs(a[4]-a[9])>1 &&
       abs(a[5]-a[6])>1 && abs(a[5]-a[8])>1 && abs(a[5]-a[9])>1 && abs(a[5]-a[10])>1 &&
       abs(a[6]-a[7])>1 && abs(a[6]-a[9])>1 && abs(a[6]-a[10])>1 &&
       abs(a[7]-a[10])>1 &&
       abs(a[8]-a[9])>1 &&
       abs(a[9]-a[10])>1)
        {
            count++;
        }
}
void Fill_numbers(int x)
{
    if(x > 10)
    {
        Check();
        return;
    }

    for(int i = 0;i<10;i++)
    {
        if(b[i] == 0)
        {
            b[i] = 1;
            a[x] = i;
            Fill_numbers(x+1);
            b[i] = 0;
        }

    }
}

int main()
{
    Fill_numbers(1);
    cout<<count;
    return 0;
}

 

 

接下来是寒假作业,请读者体会这两者思路上的差异。

问题描述:

寒假作业


现在小学的数学题目也不是那么好玩的。
看看这个寒假作业:


   □ + □ = □
   □ - □ = □
   □ × □ = □
   □ ÷ □ = □
   

   (如果显示不出来,可以参见下图)

   
每个方块代表1~13中的某一个数字,但不能重复。
比如:
 6  + 7 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5


以及: 
 7  + 6 = 13
 9  - 8 = 1
 3  * 4 = 12
 10 / 2 = 5


就算两种解法。(加法,乘法交换律后算不同的方案)
 
你一共找到了多少种方案?


请填写表示方案数目的整数。

注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

 

对于这个问题我们很容易联想到上面那种解法,即:

#include <iostream>
#define _MAX 13

using namespace std;

int count = 0;
int num[_MAX];
int vis[_MAX];

void dfs(int n)
{
    int i = 0;
    if (n >= _MAX)
    {
        if (num[0] + num[1] == num[2] && num[3] - num[4] == num[5]&&num[6] * num[7] == num[8]&&num[10] * num[11] == num[9])
            count++;
        return ;
    }
    for (; i < _MAX; i++)
    {
        if (!vis[i])
        {
            vis[i] = 1;
            num[n] = i + 1;
            dfs(n + 1);
            vis[i] = 0;
        }
    }
    return ;
}

int main()
{
    dfs(0);
    cout<<count;
    return 0;
}

 

 

这种做法当然是正确的,但是如果你运行了一遍就会知道,它要得出正确的结果,需要长达几分钟的运行时间,因为我们是在所有数都填好之后才开始进行判断,而且这里有12个空,13个数字,因此大大增加了计算量,因此,我们需要优化算法,让它不要进行多余的计算,也就是说,如果第一个表达式都不满足的情况下,以后所有后续的填数工作我们都不需要做了。即:

#include <iostream>
#define _MAX 13

using namespace std;

int count = 0;
int num[_MAX];
int vis[_MAX];

int Check(int n)
{
    if (n == 2)
    {
        if (num[0] + num[1] == num[2])
        {
            return 1;
        }
    }
    else if (n == 5)
    {
        if (num[3] - num[4] == num[5])
        {
            return 1;
        }
    }
    else if (n == 8)
    {
        if (num[6] * num[7] == num[8])
        {
            return 1;
        }
    }
    else if (n == 11)
    {
        if (num[10] * num[11] == num[9])
        {
            count++;
            return 1;
        }
    }
    else
    {
        return 1;
    }
    return 0;
}

void dfs(int n)
{
    int i = 0;
    if (n >= _MAX)
    {
        return ;
    }
    for (; i < _MAX; i++)
    {
        if (!vis[i])
        {
            vis[i] = 1;
            num[n] = i + 1;
            if (!Check(n)) 
            {
                vis[i] = 0;
                continue;
            }
            dfs(n + 1);
            vis[i] = 0;
        }
    }
    return ;
}

int main()
{
    dfs(0);
    cout<<count;
    return 0;
}

 

posted @ 2018-02-20 14:58  Nikki_o3o  阅读(287)  评论(0编辑  收藏  举报