问题描述
用2×1或1×2的骨牌把m×n的棋盘完全覆盖是一个很容易解决的问题。那么,能不能把完全覆盖的种数求出来会不会很复杂呢?请给出所有各不相同覆盖方案。
棋盘的完美覆盖又称多米诺覆盖(Domino Tiling),是组合数学中一个颇有趣味的问题。首先介绍与该问题相关的一些基本概念:
多米诺(Domino):一个多米诺是由两个单位正方形拼接而成的 1\times2 或 2\times1 矩形。
多米诺覆盖(Domino Tiling): 如果一些多米诺恰好互不重叠地覆盖一个 m\times n 棋盘,则此覆盖称为一个多米诺覆盖。
作者:心露边白
链接:https://www.jianshu.com/p/14dbb6121ac3
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
要求
给定任意m行n列的棋盘,编写程序求出使用2×1或1×2的骨牌对棋盘进行完全覆盖有多少种不同的方法。
解题思路
采用回溯法从左上角至右下角依次扫描未放置骨牌的方格,根据方格的右方和下方是否已经放置了骨牌来判断放置骨牌的位置以达到求解的目的。
在存储方面,用 bool A[m][n]
存储 m\times n 棋盘,每一次加入新的多米诺时,就将相邻的一对小方格上的 bool
值设置为 1
(表示占用)。
以3x4棋盘为例
此时填满棋盘,计数+1并进行回溯
代码如下
#include <iostream>
using namespace std;
int m,n; // 长宽
bool **Chess; // 棋盘(使用bool型,前面的两个**表示定义了一个二维的bool型数组)
int Count=0; // 计数
void Domino(int i,int j){
if(i==m){
puts("______________");
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j){
cout<<Chess[i][j]<<" ";
}
puts("");
}
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;
cout<<Chess[i]<<endl;
}
puts("______________");
for(int i=0;i<m;++i)
{
for(int j=0;j<n;++j){
cout<<&Chess[i][j]<<" ";
}
puts("");
}
puts("______________");
Domino(0,0);
cout<<endl;
cout<<Count<<endl;
return 0;
}
代码运行结果
其中—————
—————内的地址为提前为执行算法占用的空间。