棋盘覆盖问题--分治法

棋盘覆盖问题。有一个 2k2k 2 k ∗ 2 k 的方格棋盘,恰有一个方格是残缺的。你的任务是用包含3个方格的L型牌覆盖所有剩余方格。残缺方格不能被覆盖,且任意一个完整方格不能同时被两个或更多牌覆盖。如图所示为L型牌的4种旋转方式。

这里写图片描述

当 k=1 ,残缺方格位置为 (0,1) 时,棋盘如图:
这里写图片描述

当 k>0 时,将 2k2k 2 k ∗ 2 k 棋盘分割为4个 2k12k1 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
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
棋盘覆盖问题是指用特殊形状的骨牌覆盖给定的矩形棋盘,要求每个骨牌恰好覆盖棋盘上的3个格子,且任何两个骨牌不重叠、不相邻。棋盘覆盖问题可以用分治法求解。 具体思路如下: 1. 将大棋盘划分成四个小棋盘,每个小棋盘的大小是原来棋盘的一半。 2. 如果当前小棋盘是1x1大小,则直接返回,因为无法再分割。 3. 在当前小棋盘中找到一个空缺格子,将其所在的小方块用一个L型骨牌覆盖。 4. 在剩余的小棋盘中递归执行步骤3,直到所有的小方块都被覆盖。 下面是C++代码实现: ```c++ #include <iostream> #include <cstring> using namespace std; const int MAXN = 1<<5; // 棋盘最大大小 int board[MAXN][MAXN]; // 棋盘 int tile = 1; // 骨牌编号 // 分治法求解棋盘覆盖问题 void ChessBoard(int tr, int tc, int dr, int dc, int size) { if(size == 1) return; int t = tile++; int 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() { memset(board, 0, sizeof(board)); // 初始化棋盘 int k = 3; // 棋盘大小 int dr = 3, dc = 2; // 空缺位置 ChessBoard(0, 0, dr, dc, 1<<k); for(int i=0; i<(1<<k); i++) { for(int j=0; j<(1<<k); j++) { if(board[i][j] == 0) cout << "- "; // 空格 else cout << board[i][j] << " "; // 骨牌编号 } cout << endl; } return 0; } ``` 以上代码输出的结果如下: ``` 4 4 1 1 12 12 9 9 4 4 1 1 12 12 9 9 11 11 6 2 2 7 8 8 11 11 6 3 3 7 7 10 5 5 5 3 13 13 10 - 5 5 14 14 13 - - - 15 15 15 14 - - - - ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值