一问题分析
将1*2型的骨牌填充到m*n的棋盘当中,使其完全覆盖而且任何骨牌不能重叠。将问题缩小成同类型子问题。
二设计思路
先从2*n和3*n的棋盘入手看其然后将其进行对更大的棋盘执行覆盖,再使用排列组合问题进行求解问题。
三算法设计
先分析2*n:数学归纳法
![](https://img-blog.csdnimg.cn/img_convert/aad676f364b1179348f2629dda9bd826.png)
归纳总结图1
2*n的骨牌的填充:
可以由2*(n-1)的骨牌最后加上一张竖2*1的骨牌,此时的填充方法有F(n-1)种。(2)还可以由2*(n-2)的骨牌最后加上2张横着的2*1的骨牌,此时的填充方法有F(n-2)种。(3)还有一种情况是由2× (n-2)的骨牌最后加上2张竖着的2×1的骨牌。总结得为斐波那契数列
奇数列不能填充,扩大最小项为两列一单位记函数f(n)f(n)f(n)为在n nn列时的覆盖方案数目,f(0)=1f(0)=1f(0)=1,f(2)f(2)f(2)我们以两列为一个单位,他与f ( 0 )的排列总数有关,而f ( 2 ) = 3 是0号位置的排列数目之和*[1-2]位置的排列方法数目,因此初始化为1.
再来看看f(4) f(4)f(4),图Ⅰ。首先考虑他最右边两列有三种情况,承上之前的排列数即f(2)*3f(2)*3f(2)∗3,四列也可能为图Ⅱ,得出f(0)*2f(0)*2f(0)*2。
![](https://img-blog.csdnimg.cn/img_convert/364db1c02b6b24e4de79d00f2804ec6a.png)
图Ⅰ 1
![](https://img-blog.csdnimg.cn/img_convert/e02fbe598c2f556fad5f2d12c5574a58.png)
图Ⅱ 1
通过递推得出f(n)=3f(n-2)+2f(n-4)+2f(n-6)+...+f(0)
![](https://img-blog.csdnimg.cn/img_convert/b1f0f87c557cd0fc0195ec1dc1ea3980.png)
算法流程图1
四代码
#include <iostream>
using namespace std;
const int maxNum = 1 << 10;
// 棋盘
int chess[maxNum][maxNum];
// L型牌编号
int number;
void chessBoard(int row, int column, int x, int y, int siz) {
// 递归出口
if(siz == 1) {
return;
}
// 对半划分成2^(siz - 1) * 2^(siz - 1)的棋盘
int s = siz / 2;
// L型牌编号自增
int t = ++number;
// 中间点,以此判别(x,y)在哪个子棋盘中
int centerRow = row + s;
int centerColumn = column + s;
// 黑色方格在左上子棋盘
if(x < centerRow && y < centerColumn) {
chessBoard(row, column, x, y, s);
} else {
// 不在则填充左上子棋盘的右下角
chess[centerRow - 1][centerColumn - 1] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessBoard(row, column, centerRow - 1, centerColumn - 1, s);
}
// 黑色方格在右上子棋盘
if(x < centerRow && y >= centerColumn) {
chessBoard(row, centerColumn, x, y, s);
} else {
// 不在则填充右上子棋盘的左下角
chess[centerRow - 1][centerColumn] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessBoard(row, centerColumn, centerRow - 1, centerColumn, s);
}
// 黑色方格在左下子棋盘
if(x >= centerRow && y < centerColumn) {
chessBoard(centerRow, column, x, y, s);
} else {
// 不在则填充左下子棋盘的右上角
chess[centerRow][centerColumn - 1] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessBoard(centerRow, column, centerRow, centerColumn - 1, s);
}
// 黑色方格在右下子棋盘
if(x >= centerRow && y >= centerColumn) {
chessBoard(centerRow, centerColumn, x, y, s);
} else {
// 不在则填充右下子棋盘的左上角
chess[centerRow][centerColumn] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessBoard(centerRow, centerColumn, centerRow, centerColumn, s);
}
}
int main() {
// 大小,黑色方格位置
int siz, x, y;
while(true) {
cout << "(x,y)从(0,0)开始,输入数据为0 0 0即结束程序。" << endl;
cout << "请输入棋盘大小和黑色方格位置(x,y):";
cin >> siz >> x >> y;
// 退出条件
if(siz == 0) {
break;
}
// 涂黑(x,y),初始化L型牌编号
chess[x][y] = number = 1;
// 分治法填满棋盘
chessBoard(0, 0, x, y, siz);
// 输出该棋盘
for(int i = 0; i < siz; i++) {
for(int j = 0; j < siz; j++) {
cout << chess[i][j] << "\t";
}
cout << endl << endl << endl;
}
}
return 0;
}