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

文章目录

  • 前言
  • 算法实现思想
  • 代码实现
  • 时间复杂度


前言

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

算法实现思想

  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小棋盘:【算法】分治法之棋盘覆盖_第4张图片
(2)特殊方格必位于4个小棋盘之一中,其余3个小棋盘中无特殊方格,为了将这3个无特殊方格的小棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个小棋盘的会合处:
【算法】分治法之棋盘覆盖_第5张图片
(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:将大棋盘分成四个小棋盘
【算法】分治法之棋盘覆盖_第6张图片
(2)Step2:四个小棋盘中必定有3个没有特殊方格,我们可以用一个L型骨牌覆盖这3个小棋盘的会合处构造出特殊方格。
【算法】分治法之棋盘覆盖_第7张图片
(3)Step3:现在我们将一个大问题分解成了四个小问题,都有特殊方格,重复Step1和Step2,直到棋盘简化为1×1棋盘
【算法】分治法之棋盘覆盖_第8张图片
(4)最终结果:
【算法】分治法之棋盘覆盖_第9张图片

代码实现

  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个小规模的问题:
【算法】分治法之棋盘覆盖_第10张图片
我们这里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

你可能感兴趣的:(算法,分治算法,算法,棋盘覆盖,java)