什么是棋盘覆盖算法?
棋盘覆盖是分治法中的一个典型问题,该问题的描述是这样的:
在一个2^k*2^k的棋盘中,有一个方格与其他方格不同,我们称这个方格为特殊方格。
然后我们要用四种不同形态的L型骨牌覆盖特殊棋盘上除了特殊方格外的所有方格,且任何两个L型骨牌不得重复覆盖。
根据数学知识,我们可以推出,使用到的L型骨牌数目为:(4^k-1)/3
如图当k=2时得一个特殊棋盘和四种不同形态的L型骨牌,黑色的为特殊方格
棋盘覆盖算法的思路和方法
棋盘覆盖实现的基本方法为分治法,是设计棋盘覆盖的一个简捷的算法,那什么是分治法?
分治法的基本思路:将一个规模为n的问题分解为k个规模较小的子问题,这些子问题相互独立且与原问题相同。递归地解决这些子问题,然后将各个子问题的解合并得到原问题的解。简单地说,就是将规模为n的问题自顶向下分解,直到子问题分解到足够小,可以容易解决时,再自底向上合并,从而得到原来的解。
当k>0时,将2^k*2^k棋盘分割为4个2^(k-1)*2^(k-1)子棋盘,如(下左)图所示:
特殊方格必定位于这四个小棋盘中,其余三个子棋盘没有特殊方格,为了将这三个无特殊方格的子棋盘转换为特殊棋盘,我们可以用一个L型骨盘覆盖这三个较小棋盘的会合处,如(下右)图所示:
从图上可以看出,这三个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将问题分解为4个较小规模的棋盘覆盖问题。递归地使用这种分割方法,直至棋盘简化为1*1棋盘,就结束递归。
实现这种算法的分析:每次都对分割后的四个小方块进行判断,判断特殊方格是否在里面。这里的判断的方法是每次先记录下整个大方块的左上角方格的行列坐标,然后再与特殊方格坐标进行比较,就可以知道特殊方格是否在该块中。如果特殊方块在里面,这直接递归下去求即可,如果不在,这根据分割的四个方块的不同位置,把右下角、左下角、右上角或者左上角的方格标记为特殊方块,然后继续递归。在递归函数里,还要有一个变量s来记录边的方格数,每次对方块进行划分时,边的方格数都会减半,这个变量是为了方便判断特殊方格的位置
什么是棋盘覆盖算法的三种方法:
棋盘覆盖算法在处理时采用的方法不同,顺序不同,得到的问题的解也会不同,(我们事先规定好以左上、右上、左下、右下的顺序处理)下面就以几张图来说明一下:
递归
队列
队列是先入先出的处理顺序,我们以一个图来画出
左左上意思是第一个队列里的左上4*4中的左上的2*2
左右上意思是第一个队列里的左上4*4中的右上的2*2
左左下意思是第一个队列里的左上4*4中的左下的2*2
左右下意思是第一个队列里的左上4*4中的右下的2*2
栈
栈是先入后出,这里我就不再画图了,和递归大概是反着来的
三种方法总结:
1三种算法的覆盖图案不同
2如果原始图中的已覆盖方格位置不同,导致覆盖图案也不同
3如果四个子问题的处理顺序不同,覆盖方案也不同
4这三种算法的效率比较:栈运行时间最短,队列运行时间最长
三种方法的源代码:
#include<stdio.h>
#include<math.h>
#include<malloc.h>
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 5
typedef struct Node{
int tr,tc,dr,dc,size;//棋盘地址
struct Node *next;
}Node;
typedef struct{
Node *front;//队头
Node *rear;//队尾
}LinkQueue;
typedef struct{
int tr,tc,dr,dc,size;
}SElemType;
typedef struct{
SElemType *base;
SElemType *top;
int stacksize;
}SqStack;
int board[8][8];
int tile=1;
int chessBoard(int tr,int tc,int dr,int dc,int size);//递归方法
int chessBoardQL(LinkQueue *Q,int x,int y);//队列算法实现
void InitQueue(LinkQueue *Q);//队列初始化
void InsertQueue(LinkQueue *Q,int tr,int tc,int dr,int dc,int size);//进队列
void DeleteQueue(LinkQueue *Q);//出队列
int LQEmpty(LinkQueue *Q);//队列判空
void Print();//打印棋盘
int InitStack(SqStack *S);//初始化栈
int StackEmpty(SqStack *S);//判空
int StackFull(SqStack *S);//判满
int Push(SqStack *S,int tr,int tc,int dr,int dc,int size);//入栈
int Pop(SqStack *S);//出栈
void DestoryStack(SqStack *S);//销毁栈
int chessBoardSS(SqStack *S,int x,int y);//栈实现
int main()
{
int x,y;
printf("请输入特殊方格的位置:");
scanf("%d%d",&x,&y);
board[x][y] = 0;
chessBoard(0,0,x,y,8);
printf("递归实现:\n");
Print();
LinkQueue Q;
InitQueue(&Q);
chessBoardQL(&Q,x,y);
printf("队列实现:\n");
Print();
SqStack S;
chessBoardSS(&S,x,y);
DestoryStack(&S);
printf("栈实现:\n");
Print();
return 0;
}
int chessBoard(int tr,int tc,int dr,int dc,int size)//递归算法
{
if(size==1)return 0;
int t = tile++;//L型骨牌号
int s = size/2;//分割棋盘;
//1.覆盖左上子棋盘
if(dr<tr+s&&dc<tc+s)//特殊方格在此子棋盘中
chessBoard(tr,tc,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s-1] = t;//用t号骨牌覆盖右下角
chessBoard(tr,tc,tr+s-1,tc+s-1,s);//覆盖其他方格
}
//2.覆盖右上子棋盘
if(dr<tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
chessBoard(tr,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s] = t;//用t号骨牌覆盖左下角
chessBoard(tr,tc+s,tr+s-1,tc+s,s);//覆盖其他方格
}
//3.覆盖左下子棋盘
if(dr>=tr+s&&dc<tc+s)//特殊方格在此子棋盘中
chessBoard(tr+s,tc,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s-1] = t;//用t号骨牌覆盖右上角
chessBoard(tr+s,tc,tr+s,tc+s-1,s);//覆盖其他方格
}
//4.覆盖右下子棋盘
if(dr>=tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
chessBoard(tr+s,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s] = t;//用t号骨牌覆盖左上角
chessBoard(tr+s,tc+s,tr+s,tc+s,s);//覆盖其他方格
}
return 0;
}
int chessBoardQL(LinkQueue *Q,int x,int y)//队列算法实现
{
int s,tr,tc,dr,dc;
int t=0;
InsertQueue(Q,0,0,x,y,8);
Node *p;
while(Q->front->next!=NULL)
{
p = Q->front->next;
if(p->size == 1){
}else{
s = p->size/2;
t++;
tr = p->tr;
tc = p->tc;
dr = p->dr;
dc = p->dc;
//1.覆盖左上子棋盘
if(dr<tr+s&&dc<tc+s)//特殊方格在此子棋盘中
InsertQueue(Q,tr,tc,dr,dc,s);//入队
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s-1] = t;//用t号骨牌覆盖右下角
InsertQueue(Q,tr,tc,tr+s-1,tc+s-1,s);
}
//2.覆盖右上子棋盘
if(dr<tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
InsertQueue(Q,tr,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s] = t;//用t号骨牌覆盖左下角
InsertQueue(Q,tr,tc+s,tr+s-1,tc+s,s);
}
//3.覆盖左下子棋盘
if(dr>=tr+s&&dc<tc+s)//特殊方格在此子棋盘中
InsertQueue(Q,tr+s,tc,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s-1] = t;//用t号骨牌覆盖右上角
InsertQueue(Q,tr+s,tc,tr+s,tc+s-1,s);
}
//4.覆盖右下子棋盘
if(dr>=tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
InsertQueue(Q,tr+s,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s] = t;//用t号骨牌覆盖左上角
InsertQueue(Q,tr+s,tc+s,tr+s,tc+s,s);
}
}
DeleteQueue(Q);//出队
}
return 0;
}
void InitQueue(LinkQueue *Q)//队列初始化
{
Q->front = (Node *)malloc(sizeof(Node));
if(!Q->front)
return;
Q->front->next = NULL;
Q->rear = Q->front;
}
void InsertQueue(LinkQueue *Q,int tr,int tc,int dr,int dc,int size)//进队列
{
Node *p;
p = (Node *)malloc(sizeof(Node));
if(!p) return;
p->next = NULL;
p->tr = tr;
p->tc = tc;
p->dr = dr;
p->dc = dc;
p->size = size;
p->next=Q->rear->next;
Q->rear->next = p;
Q->rear=Q->rear->next;
}
void DeleteQueue(LinkQueue *Q)//出队列
{
Node *p;
if(Q->front == Q->rear)
return;
p = Q->front->next;
Q->front->next = p->next;
free(p);
}
int LQEmpty(LinkQueue *Q)//队列判空
{
if(Q->front == Q->rear)
return true;
return false;
}
void Print(){//打印棋盘
printf("-------------------------\n");
for(int i=0;i<8;i++){
printf("|");
for(int j=0;j<8;j++)
printf("%2d|",board[i][j]);
printf("\n-------------------------\n");
}
}
int chessBoardSS(SqStack *S,int x,int y)
{
if(InitStack(S)==false)
return 0;
Push(S,0,0,x,y,8);
int s,tr,tc,dr,dc;
int t=0;
while(!StackEmpty(S))//栈不空
{
if(S->top->size==1){
Pop(S);//出栈
}else{
t++;
s=S->top->size/2;
tr=S->top->tr;
tc=S->top->tc;
dr=S->top->dr;
dc=S->top->dc;
Pop(S);//出栈
//1.覆盖左上子棋盘
if(dr<tr+s&&dc<tc+s)//特殊方格在此子棋盘中
Push(S,tr,tc,dr,dc,s);//入栈
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s-1] = t;//用t号骨牌覆盖右下角
Push(S,tr,tc,tr+s-1,tc+s-1,s);
}
//2.覆盖右上子棋盘
if(dr<tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
Push(S,tr,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s-1][tc+s] = t;//用t号骨牌覆盖左下角
Push(S,tr,tc+s,tr+s-1,tc+s,s);
}
//3.覆盖左下子棋盘
if(dr>=tr+s&&dc<tc+s)//特殊方格在此子棋盘中
Push(S,tr+s,tc,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s-1] = t;//用t号骨牌覆盖右上角
Push(S,tr+s,tc,tr+s,tc+s-1,s);
}
//4.覆盖右下子棋盘
if(dr>=tr+s&&dc>=tc+s)//特殊方格在此子棋盘中
Push(S,tr+s,tc+s,dr,dc,s);
else{//特殊方格不在此子棋盘中
board[tr+s][tc+s] = t;//用t号骨牌覆盖左上角
Push(S,tr+s,tc+s,tr+s,tc+s,s);
}
}
}
return 0;
}
int InitStack(SqStack *S)//初始化栈
{
S->base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!S->base)
return false;
S->top=S->base;
S->stacksize=STACK_INIT_SIZE;
return true;
}
int StackEmpty(SqStack *S)//判空
{
if(S->top==S->base)
return true;
return false;
}
int StackFull(SqStack *S)//判满
{
if(S->top-S->base>=S->stacksize)
return true;
return false;
}
int Realloc(SqStack *S)//重新分配空间
{
S->base=(SElemType *)realloc(S->base,(STACK_INIT_SIZE+STACKINCREMENT)*sizeof(SElemType));
if(!S->base)
return false;
S->top=S->base+S->stacksize;
S->stacksize+=STACKINCREMENT;
return true;
}
int Push(SqStack *S,int tr,int tc,int dr,int dc,int size)//入栈
{
if(StackFull(S)==true)
if(Realloc(S)==false)
return false;
S->top++;
S->top->tr=tr;
S->top->tc=tc;
S->top->dr=dr;
S->top->dc=dc;
S->top->size=size;
//*S->top++ = elem;
return true;
}
int Pop(SqStack *S)//出栈
{
if(StackEmpty(S)==true)
return false;
S->top--;
return true;
}
void DestoryStack(SqStack *S)//销毁栈
{
S->top=S->base;
S->base=NULL;
S->stacksize=0;
free(S->base);
}