棋盘覆盖问题。有一个 2k∗2k 2 k ∗ 2 k 的方格棋盘,恰有一个方格是残缺的。你的任务是用包含3个方格的L型牌覆盖所有剩余方格。残缺方格不能被覆盖,且任意一个完整方格不能同时被两个或更多牌覆盖。如图所示为L型牌的4种旋转方式。
当 k=1 ,残缺方格位置为 (0,1) 时,棋盘如图:
当 k>0 时,将 2k∗2k 2 k ∗ 2 k 棋盘分割为4个 2k−1∗2k−1 2 k − 1 ∗ 2 k − 1 子棋盘。
特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,我们可以用一个L型骨牌覆盖这3个较小的棋盘的汇合处,如下图所示,这3个子棋盘上被L型骨牌覆盖的方格就成为该棋盘上的特殊方格,从而将原问题化为4个较小规模的棋盘覆盖问题。递归的使用这种分割,直至棋盘简化为1x1棋盘。
分治三步骤
- 划分问题:将2k∗2k2k∗2k的棋盘划分为2k−1∗2k−12k−1∗2k−1这样的子棋盘4块。
- 递归求解:递归填充各个格子,填充分为四个情况,在下面会有解释,递归出口为k=0k=0也就是子棋盘方格数为1。
- 合并问题:不需要合并子问题。
递归填充的四种情况
- 如果残缺方块在左上子棋盘,则递归填充左上子棋盘;否则填充左上子棋盘的右下角,将右下角看做残缺方块,然后递归填充之。
- 如果残缺方块在右上子棋盘,则递归填充右上子棋盘;否则填充右上子棋盘的左下角,将左下角看做残缺方块,然后递归填充之。
- 如果残缺方块在左下子棋盘,则递归填充左下子棋盘;否则填充左下子棋盘的右上角,将右上角看做残缺方块,然后递归填充之。
- 如果残缺方块在右下子棋盘,则递归填充右下子棋盘;否则填充右下子棋盘的右下角,将左上角看做残缺方块,然后递归填充之。
import java.util.Scanner;
public class Main {
static int[][] chess=new int[1000][1000];
static int number=1;//L型骨牌编号
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k=sc.nextInt();//输入k,棋盘大小为 2^k*2^k
int x=sc.nextInt();//输入残缺块的坐标
int y=sc.nextInt();
int chessSize=(int) Math.pow(2, k);//边长为 2^k
if(chessSize==0||x>=chessSize||y>=chessSize){//输入非法
System.out.println("input error");
return;
}
//标记残缺位置(x,y) =-1
chess[x][y]=-1;
//分治 递归 填满棋盘
chessFill(0,0,x,y,chessSize);
//输出填充结果
for(int i=0;i<chessSize;i++){
for(int j=0;j<chessSize;j++){
System.out.print(chess[i][j]);
if(j<chessSize-1){
System.out.print("\t");
}
}
System.out.println();//输出每行回车
System.out.println();
}
}
/**
* @param i 子棋盘相对于原最大棋盘chess[][] 的起始位置
* @param j
* @param x 残缺点位置
* @param y
* @param chessSize 子棋盘大小
*/
private static void chessFill(int row, int column, int x, int y, int chessSize) {
// 递归出口
if (chessSize == 1) {
return;
}
// 对半划分成2^(chessSize - 1) * 2^(chessSize - 1)的棋盘
int s = chessSize / 2;
// L型牌编号自增
int t = number++;
// 中间点,以此判别(x,y)在哪个子棋盘中
int centerRow = row + s;
int centerColumn = column + s;
// 黑色方格在左上子棋盘
if (x < centerRow && y < centerColumn) {
chessFill(row, column, x, y, s);
} else {
// 不在则填充左上子棋盘的右下角
chess[centerRow - 1][centerColumn - 1] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessFill(row, column, centerRow - 1, centerColumn - 1, s);
}
// 黑色方格在右上子棋盘
if (x < centerRow && y >= centerColumn) {
chessFill(row, centerColumn, x, y, s);
} else {
// 不在则填充右上子棋盘的左下角
chess[centerRow - 1][centerColumn] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessFill(row, centerColumn, centerRow - 1, centerColumn, s);
}
// 黑色方格在左下子棋盘
if (x >= centerRow && y < centerColumn) {
chessFill(centerRow, column, x, y, s);
} else {
// 不在则填充左下子棋盘的右上角
chess[centerRow][centerColumn - 1] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessFill(centerRow, column, centerRow, centerColumn - 1, s);
}
// 黑色方格在右下子棋盘
if (x >= centerRow && y >= centerColumn) {
chessFill(centerRow, centerColumn, x, y, s);
} else {
// 不在则填充右下子棋盘的左上角
chess[centerRow][centerColumn] = t;
// 然后覆盖其他格子,注意这时(x,y)要看做已填充位置
chessFill(centerRow, centerColumn, centerRow, centerColumn, s);
}
}
}
输入:
1 0 0
输出:
-1 1
1 1
输入:
2 3 3
输出:
2 2 3 3
2 1 1 3
4 1 5 5
4 4 5 -1