算法,新开的课之一,老师也还挺有意思,所以听课的积极性也较高些。其中摘取了递归与分治内容中的棋盘覆盖问题。
先看下问题:。
在一个2^k * 2^k 个方格组成的棋盘中,给定任意一个特殊方格,用四种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠。
对于图中给出的k为2例子,可以试出骨牌放置的位置。但当k值更大时就尴尬了。我们都希望把大事化小,不难发现,当一个特殊方格存在于一个2*2的棋盘内,L型骨牌的排放位置是唯一的。那么以上图为例,可以确定左上角的骨牌,则整个棋盘的左上角也随之确定。
进而我们希望把其他部分可以与左上角一样容易确定,所以利用分治的方法,将棋盘分为4个2*2的小棋盘,希望能达到与左上部分类似的情况,这时又可以发现,由于左上部分存在特殊方格,而除左上部分后,中间又有三个方格可以组成L型,将这三个方格视为各自2*2的特殊方格后,各自方格内又出现与左上类似的情况。
所以我们以此类推,2^k*2^k的棋盘可依次4分,每一次设最中间除特殊方格所在子棋盘外的3个方格为特殊方格,直至子棋盘为2*2。
代码实现:
对于一个语言掌握不太熟练的人来说,实现一个实际问题的代码可以说时很头疼的一件事。
我想首先必须要做的就是判断特殊方格在哪个位置,而对比的方法则可以通过最左上角的方格的位置和当前子棋盘的行列数的和进行比对(当然笔者认为如果用其他方格位置进行比对也可以,只是左上角更简便些),如果在左上子棋盘,则递归即可,若不在,则需要将之前所说的3个方格标为特殊方格。
标记时为了方便区分,每次标记时用不同数字,这个在每次递归一开始设置一个+1的变量即可。
处理完这些,大概思路也就清晰了,最终代码如下。
#include <stdio.h> #include <stdlib.h> int tile=1; int Board[100][100]; void ChessBoard(int tr,int tc,int dr,int dc,int size) { int s,t; if(size==1) return; t=tile++; s=size/2; if(dr<tr+s&&dc<tc+s) ChessBoard(tr,tc,dr,dc,s); else { Board[tr+s-1][tc+s-1]=t; ChessBoard(tr,tc,tr+s-1,tc+s-1,s); } if(dr<tr+s&&dc>=tc+s) ChessBoard(tr,tc+s,dr,dc,s); else { Board[tr+s-1][tc+s]=t; ChessBoard(tr,tc+s,tr+s-1,tc+s,s); } if(dr>=tr+s&&dc<tc+s) ChessBoard(tr+s,tc,dr,dc,s); else { Board[tr+s][tc+s-1]=t; ChessBoard(tr+s,tc,tr+s,tc+s-1,s); } if(dr>=tr+s&&dc>=tc+s) ChessBoard(tr+s,tc+s,dr,dc,s); else { Board[tr+s][tc+s]=t; ChessBoard(tr+s,tc+s,tr+s,tc+s,s); } } int main() { int i,j,m,tr=0,tc=0,dr=0,dc=1,size; printf("请输入棋盘行列的m值:"); scanf("%d",&m); size=m; printf("棋盘行列数为%d\n",m); memset(Board,0,sizeof(Board)); ChessBoard(tr,tc,dr,dc,size); for(i=0;i<m;i++) { for(j=0;j<m;j++) printf("%d ",Board[i][j]); printf("\n"); } return 0; }
因为本人水平有限,仅是算法的初学者,文中内容难免有错误不足,恳请各位批评改正,也欢迎与我一同交流,共同学习进步。