1. 问题描述
2. 求解思路
① 当k=0时,如图(a),只有一个特殊方格;当k=1时,如图(b),(c),(d),(e),有四种不同的棋盘
② 当k较大时,可以利用分治法的思想,将棋盘从中间一分为四,为了将其划分为四个相同的子问题,可以在分界处给无特殊方格的三个子问题补充一个特殊方格(即补充一个三格骨牌),然后继续按上述规则对子问题进行划分,直到k=1时,问题解便很容易得出。
3. 算法实现
1. 数据结构设计
- 棋盘:整型二维数组board[size][size],其中size= 2 k 2^k 2k
- 子棋盘:由棋盘左上角的下标 tr, tc 和棋盘大小 s 表示
- 特殊方格:用board[dr][dc]表示特殊方格,dr和dc为二维数组下标
- 三格骨牌:用全局整型变量tile表示,从1开始连续编号1~( 4 k 4^k 4k-1)/3,board中3个相同的整数表示一个三格骨牌
2. 分治算法
#include <stdio.h>
#define MAXSIZE 1025
//问题表示
int k; //棋盘大小
int x, y; //特殊方格的位置
//求解问题表示
int board[MAXSIZE][MAXSIZE];
int tile = 1;
void TileBoard(int tr, int tc, int dr, int dc, int size) {
if (size == 1) return; //递归出口
int t = tile++; //取一个三格骨牌,其牌号为tile
int s = size / 2; //分割棋盘
//处理左上角象限的子棋盘
if (dr < tr + s && dc < tc + s) //特殊方格在此象限中
TileBoard(tr, tc, dr, dc, s);
else { //此象限中无特殊方格
board[tr + s - 1][tc + s - 1] = t; //用t号三格骨牌覆盖右下角
TileBoard(tr, tc, tr + s - 1, tc + s - 1, s); //将右下角作为特殊方格继续处理该象限
}
//处理右上角象限的子棋盘
if (dr < tr + s && dc >= tc + s) //特殊方格在此象限中
TileBoard(tr, tc + s, dr, dc, s);
else { //此象限中无特殊方格
board[tr + s - 1][tc + s] = t; //用t号三格骨牌覆盖左下角
TileBoard(tr, tc + s, tr + s - 1, tc + s, s); //将左下角作为特殊方格继续处理该象限
}
//处理左下角象限的子棋盘
if (dr >= tr + s && dc < tc + s) //特殊方格在此象限中
TileBoard(tr + s, tc, dr, dc, s);
else { //此象限中无特殊方格
board[tr + s][tc + s - 1] = t; //用t号三格骨牌覆盖右上角
TileBoard(tr + s, tc, tr + s, tc + s - 1, s); //将右上角作为特殊方格继续处理该象限
}
//处理右下角象限的子棋盘
if (dr >= tr + s && dc >= tc + s) //特殊方格在此象限中
TileBoard(tr + s, tc + s, dr, dc, s);
else { //此象限中无特殊方格
board[tr + s][tc + s] = t; //用t号三格骨牌覆盖左上角
TileBoard(tr + s, tc + s, tr + s, tc + s, s); //将左上角作为特殊方格继续处理该象限
}
}
void main() {
int k;
printf("请输入k值,棋盘的大小为2的k次方:");
scanf_s("%d", &k);
int size = 1 << k; //size=2的k次方
int x, y; //(x,y)为特殊方格的下标
printf("请输入x值,特殊方格的横坐标为:");
scanf_s("%d", &x);
printf("请输入y值,特殊方格的纵坐标为:");
scanf_s("%d", &y);
board[x][y] = 0; //初始特殊方格标号为0
TileBoard(0, 0, x, y, size);
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++)
printf("%4d", board[i][j]);
printf("\n");
}
}