1问题分析
1.1问题描述:
用2×1或1×2的骨牌把m×n的棋盘完全覆盖是一个很容易解决的问题,把完全覆盖的种数求出来,解出给出所有各不相同覆盖方案。
给定任意m行n列的棋盘,编写程序求出使用2×1或1×2的骨牌对棋盘进行完全覆盖有多少种不同的方法。
2. 问1.2基本要求:题的解决
2.1解决方案
在棋盘填充问题中,当问题规模很大时侯,我们很难想到一般思路,对此我们可以将问题小化,对进行小化的问题开始分析。
2.2算法选择
排列组合算法
2.3设计思路
现在我们将问题简化一下,2*n和3*n的棋盘,分别有多少种完美覆盖?然后将其进行对更大的棋盘执行覆盖,再使用排列组合问题进行求解问题。
3.问题解决及代码
3.12*n的设计:
首先看2*n:
提供一个简易化的问题:在2×n的一个长方形方格中,用一个2× 1的骨牌铺满方格,例如n=3时,为2× 3方格,骨牌的铺放方案有三种(如图)。求输入n,输出铺放方案的总数。
骨牌的铺放方案
分析:
可以把2× 1的骨牌看成一个整体,可以竖着放,也可以横着放。
2× n的骨牌的填充:
(1)可以由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的骨牌,但这已经包括在(1)中,所以不用再加一遍了。
最后总的填充方法有:F(n)=F(n-1)+F(n-2),即斐波那契数列。可以很容易写出它的代码:
#include<stdio.h>
void main()
{
int n,i;
int a[100];
printf("请输入方格的长度n:");
scanf("%d",&n);
a[0]=1;
a[1]=1;
for(i=2;i<=n;i++)
{
a[i]=a[i-2]+a[i-1];
}
printf("共有 %d 种填法\n",a[n]);
//斐波那契数列1,2,3,5,8,13,21,34,55,89,144...
}
3.23*n的设计:
n 是奇数,可以考虑一列,三列,5列的情形,你会发现,只要是奇数列,我们完全没有办法把他填充完整,因此我们可以考虑以两列为一个单位。
记函数f ( n ) f(n)f(n)为在n nn列时的覆盖方案数目,f ( 0 ) = 1 f(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 ) ∗ 3 f(2)*3f(2)∗3,不止如此,他的四列也可能长蓝线框起来这样,这种情况下有几种组合呢,答案是f ( 0 ) ∗ 2 f(0)*2f(0)∗2。
最后通过不断地递推,可以得到如下公式:
由此,可以得到代码:
#include <stdio.h>
int main()
{
unsigned long a[100] = { 3, 11 };
int i = 0;
for( i = 2; i < 100; i++ )
a[i] = a[i-1] * 4 - a[i-2];
while( scanf( "%d", &i ) && i!=0)
{
if( i % 2 == 0 ) printf ( "%d\n", a[i/2-1] );
else printf( "0\n" );
}
return 0;
}
3.3m*n代码实现:
依据上面所探索的,可以写出:
m*n的棋盘代码:
#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;
}
目录