美文网首页
棋盘的多米诺覆盖方法数计算(C++实现)

棋盘的多米诺覆盖方法数计算(C++实现)

作者: 心露边白 | 来源:发表于2018-07-13 16:47 被阅读0次

问题介绍

棋盘的完美覆盖又称多米诺覆盖(Domino Tiling),是组合数学中一个颇有趣味的问题。首先介绍与该问题相关的一些基本概念:

  • 多米诺(Domino):一个多米诺是由两个单位正方形拼接而成的 1\times22\times1 矩形。
  • 多米诺覆盖(Domino Tiling): 如果一些多米诺恰好互不重叠地覆盖一个 m\times n 棋盘,则此覆盖称为一个多米诺覆盖。

下图是 4\times4 棋盘的四种多米诺覆盖.

4×4 棋盘的多米诺覆盖
下面的定理回答了什么样的棋盘存在多米诺覆盖:

定理: m\times n 棋盘存在多米诺覆盖当且仅当 mn 是偶数。

这一结论很容易证明:如果 mn 之一为偶数(不妨设为 m),则每一列都恰好可以被 \frac{m}2 个多米诺覆盖。反之,如果 m,n 均为奇数,则棋盘含有奇数个方格, 因而不存在多米诺覆盖。

然而,一个更具挑战性的问题是:对于给定的正整数 m,nm\times n 棋盘共有多少种不同的多米诺覆盖?Kasteleyn 于 1961 年证明了如下结果:

定理 (Kasteleyn, 1961):m 是偶数,则 m\times n 棋盘的多米诺覆盖方法数为
Q_{m,n}=\prod_{k=1}^{\frac m2}\prod_{l=1}^n2\sqrt{\cos^2\frac{k\pi}{m+1}+\cos^2\frac{l\pi}{n+1}}

这个惊为天人的等式揭示了多米诺覆盖问题背后的秘密。例如,8\times 8 棋盘的多米诺覆盖方法数是 12988816=2^4\cdot17^2\cdot 53^2

算法设计

下面我们将利用编程来计算多米诺覆盖的方法数。与八皇后问题类似,计算该问题使用的方法是递归回溯(而不是上面的定理),具体说来,就是从棋盘左上角依次扫描尚未填入多米诺的小方格,然后根据方格下方和右边的空格决定下一个多米诺的摆放位置。

在存储方面,用 bool A[m][n] 存储 m\times n 棋盘,每一次加入新的多米诺时,就将相邻的一对小方格上的 bool 值设置为 1 (表示占用)。

代码实现

有了以上的准备以后,就可以用代码来实现多米诺覆盖方法数的计算了。C++代码如下:

#include <iostream>

using namespace std;

int m,n; // 长宽
bool **Chess; // 棋盘
int Count=0; // 计数
 
void Domino(int i,int j){
    if(i==m){Count++;return;} // 完成
    if(j==n-1){ // 当前方格位于最后一列
        if(Chess[i][j]==0){ // 尚未放置
            if(i!=m-1&&Chess[i+1][j]==0){ // 下方可以放置
                Chess[i][j]=Chess[i+1][j]=1; // 放置
                Domino(i+1,0); // 换行
                Chess[i][j]=Chess[i+1][j]=0; // 恢复
            }
        }
        else Domino(i+1,0); // 换行
    }
    else{ // 当前方格不在最后一列
        if(Chess[i][j]==0){ // 尚未放置
            if(i!=m-1&&Chess[i+1][j]==0){ // 下方可以放置
                Chess[i][j]=Chess[i+1][j]=1; // 放置
                Domino(i,j+1); // 右移
                Chess[i][j]=Chess[i+1][j]=0; // 恢复
            }
            if(Chess[i][j+1]==0){ // 右边可以放置
                Chess[i][j]=Chess[i][j+1]=1; // 放置
                Domino(i,j+1); // 右移
                Chess[i][j]=Chess[i][j+1]=0; // 恢复
            }
        }
        else Domino(i,j+1); // 右移
    }
}

int main(){
    cin >> m >> n;
    bool *p=new bool[m*n];
    for(int i=0;i<m*n;i++)p[i]=0;
    Chess=new bool*[m];
    for(int i=0;i<m;i++)Chess[i]=p+i*n;
    Domino(0,0);
    cout << Count << endl;
    delete []Chess;
    delete []p;
    return 0;
}

输入 8 8,得到的结果恰好是 12988816

参考资料

[1] Domino Tiling: http://math.uchicago.edu/~may/REU2015/REUPapers/Borys.pdf

相关文章

  • 棋盘的多米诺覆盖方法数计算(C++实现)

    问题介绍 棋盘的完美覆盖又称多米诺覆盖(Domino Tiling),是组合数学中一个颇有趣味的问题。首先介绍与该...

  • Java基于分治算法实现的棋盘覆盖问题示例

    Java基于分治算法实现的棋盘覆盖问题示例 本文主要介绍了ava基于分治算法实现的棋盘覆盖问题,简单描述了棋盘覆盖...

  • 稳定/免分割线多米诺骨牌的棋盘覆盖问题

    前言 多米诺骨牌的棋盘覆盖问题是一类经典的数学/算法问题。本文讨论添加了免分割线约束条件的多米诺骨牌覆盖问题。 问...

  • 棋盘覆盖问题(C++)

  • 棋盘覆盖

    题目描述:如下图 思路如下路 图展示跟清晰一些 根据伪代码做出实际的编码: 添加一个main 函数 测试上...

  • 阶梯棋盘不能完全多米诺完美覆盖的证明

    证明:将棋盘由上到下依次涂上黑白色. 处于阶梯最外面一格的均为黑色,与他们相邻的是白色. 最外层黑色的格子数量是n...

  • 棋盘覆盖问题

    这个题要用到分治和递归的技巧:分治的技巧在于如何划分棋盘,使划分后的子棋盘的大小相同,并且每个子棋盘均包含一个特殊...

  • 棋盘覆盖问题

    Tags: 算法 棋盘覆盖问题 【问题描述】 在一个2^k×2^k个方格组成的棋盘中,若有一个方格与其他方格不同,...

  • 棋盘覆盖(递归)

    原创 棋盘覆盖问题 算法设计思想: 因为棋盘大小是大小,所以可以进行四等分,即分成左上,右上,左下,右下四个区,而...

  • 搜索专题整理

    A - 棋盘问题 (POJ 1321) 题意 在一个n*n的棋盘上放置k个棋子,棋子不能同行同列。求方法数。 思路...

网友评论

      本文标题:棋盘的多米诺覆盖方法数计算(C++实现)

      本文链接:https://www.haomeiwen.com/subject/ztfspftx.html