【算法】分治法之棋盘覆盖


前言

  1. 有关分治算法思想文章指路:【算法】分治算法
  2. 什么是棋盘覆盖问题?
    (1)在一个 2 k × 2 k 2^k×2^k 2k×2k 个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为一特殊方格,且称该棋盘为一特殊棋盘。
    (2)在棋盘覆盖问题中,要用以下4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖。
    在这里插入图片描述
    (3)例如:在这里插入图片描述
    我们需要用上面4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖,如下图:在这里插入图片描述

算法实现思想

  1. 问题分析:

(1)当k>0时,将 2 k × 2 k 2^k×2^k 2k×2k棋盘分割为4个 2 k − 1 2^{k-1} 2k1× 2 k − 1 2^{k-1} 2k1小棋盘:在这里插入图片描述
(2)特殊方格必位于4个小棋盘之一中,其余3个小棋盘中无特殊方格,为了将这3个无特殊方格的小棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个小棋盘的会合处:
在这里插入图片描述
(3)将原问题转化为4个较小规模的棋盘覆盖问题
(4)递归地使用这种分割,直至棋盘简化为1×1棋盘

  1. 分治算法思想:
    我们把一个 2 k × 2 k 2^k×2^k 2k×2k有一个特殊方格的棋盘分成了4个都有一个特殊方格 2 k − 1 2^{k-1} 2k1× 2 k − 1 2^{k-1} 2k1小棋盘,然后递归分解,直至棋盘简化为1×1棋盘。

  2. 具体过程:

(1)Step1:将大棋盘分成四个小棋盘
在这里插入图片描述
(2)Step2:四个小棋盘中必定有3个没有特殊方格,我们可以用一个L型骨牌覆盖这3个小棋盘的会合处构造出特殊方格。
在这里插入图片描述
(3)Step3:现在我们将一个大问题分解成了四个小问题,都有特殊方格,重复Step1和Step2,直到棋盘简化为1×1棋盘
在这里插入图片描述
(4)最终结果:
在这里插入图片描述

代码实现

  1. 实现思路
    根据棋盘特殊方格的位置我们可以分成4种情况,也就是我们把大棋盘分成4个小棋盘的时候判断:

(1)如果特殊方格在左上角小棋盘中,我们可以直接以左上角的特殊方格继续分解覆盖,特殊方格没有在左上角中,我们是不是需要覆盖左上角棋盘与其他小棋盘汇合处的方格,然后以这个方格作为特殊方格继续分解覆盖,左上角棋盘与其他小棋盘汇合处的方格就是左上角棋盘最右下角的方格。

(2)如果特殊方格在右上角小棋盘中,我们可以直接以右上角的特殊方格继续分解覆盖,特殊方格没有在右上角中,我们是不是需要覆盖右上角棋盘与其他小棋盘汇合处的方格,然后以这个方格作为特殊方格继续分解覆盖,右上角棋盘与其他小棋盘汇合处的方格就是右上角棋盘最左下角的方格。

(3)如果特殊方格在左下角小棋盘中,我们可以直接以左下角的特殊方格继续分解覆盖,特殊方格没有在左下角中,我们是不是需要覆盖左下角棋盘与其他小棋盘汇合处的方格,然后以这个方格作为特殊方格继续分解覆盖,左下角棋盘与其他小棋盘汇合处的方格就是左下角棋盘最右上角的方格。

(4)如果特殊方格在右下角小棋盘中,我们可以直接以右下角的特殊方格继续分解覆盖,特殊方格没有在右下角中,我们是不是需要覆盖右下角棋盘与其他小棋盘汇合处的方格,然后以这个方格作为特殊方格继续分解覆盖,右下角棋盘与其他小棋盘汇合处的方格就是右下角棋盘最左上角的方格。

结束条件是棋盘大小为1×1棋盘。

  1. 代码实现:
public class Demo {
    public static int num = 0;


    public static void main(String[] args) {
        int[][] board = {{0,-1,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}};
        System.out.println("需要覆盖的棋盘:");
        for (int[] ints : board) {
            for (int anInt : ints) {
                System.out.print(anInt+" ");
            }
            System.out.println();
        }

        chessBoard(board,0,0,0,1,4);
        System.out.println("覆盖后的棋盘:");
        for (int[] ints : board) {
            for (int anInt : ints) {
                System.out.print(anInt+" ");
            }
            System.out.println();
        }

    }

    /**
     *
     * @param board 棋盘
     * @param tr 棋盘的最左上角方格的 x坐标
     * @param tc  棋盘的最左上角方格的 y坐标
     * @param dr  特殊方格的 x坐标
     * @param dc   特殊方格的 y坐标
     * @param size  棋盘的大小
     */
    public static void chessBoard(int[][] board,int tr, int tc, int dr, int dc, int size){


        //棋盘大小为1 x 1 ==》结束
        if (size == 1){
            return;
        }

        //棋盘大小的一半:
        int s = size/2;

        //覆盖的号码:
        int t = ++num;

        //左上角的判断:
        if (dr < tr+s && dc < tc+s){
            chessBoard(board,tr, tc, dr, dc, s);
        }else {
            board[tr+s-1][tc+s-1] = t;
            chessBoard(board,tr,tc,tr+s-1,tc+s-1,s);
        }

        //右上角
        if (dr < tr+s && dc >= tc+s){
            chessBoard(board,tr, tc+s, dr, dc, s);
        }else {
            board[tr+s-1][tc+s] = t;
            chessBoard(board,tr,tc+s,tr+s-1,tc+s,s);
        }

        //左下角的判断:
        if (dr >= tr+s && dc < tc+s){
            chessBoard(board,tr+s, tc, dr, dc, s);
        }else {
            board[tr+s][tc+s-1] = t;
            chessBoard(board,tr+s,tc,tr+s,tc+s-1,s);
        }

        //右下角
        if (dr >= tr+s && dc >= tc+s){
            chessBoard(board,tr+s, tc+s, dr, dc, s);
        }else {
            board[tr+s][tc+s] = t;
            chessBoard(board,tr+s,tc+s,tr+s,tc+s,s);
        }

    }
}

输出:我们把特殊方格放在了(0,1)位置

需要覆盖的棋盘:
0 -1 0 0 
0 0 0 0 
0 0 0 0 
0 0 0 0 
覆盖后的棋盘:
2 -1 3 3 
2 2 1 3 
4 1 1 5 
4 4 5 5 

时间复杂度

可以用分治法的主定律公式,每次分解成4个小规模的问题:
在这里插入图片描述
我们这里k = l o g 2 n log_{2}n log2n ,因为我们把n x n的棋盘看成 2 k × 2 k 2^k×2^k 2k×2k ,自然k = l o g 2 n log_{2}n log2n

  • 21
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值