1. 程式人生 > >藍橋杯之方格填數,寒假作業

藍橋杯之方格填數,寒假作業

問題描述:

方格填數


如下的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;
}