分治,循环赛日常表、棋盘覆盖

分治,字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

循环赛日常表

2的n次幂支球队打球,否则不适合分治。

2的n次幂支球队,循环比赛,保证每只球队互相都打过一场。纵坐标球队名称,横坐标是第几天,球队1,在七天中分别和234568球队比赛,球队2在七天中依次和1436587球队比赛。

如果像篮球圣诞大战,要给特殊队伍设置比赛,只需要把队的顺序换下,比如湖人本来是2,换成3

根据结果,推导算法。上图中有如下规律

  • 假设等分为4个矩阵,矩阵边距4,右上角矩阵,比左上角矩阵,同一行同一列大4,如第一行,右上角第一列是5左上角第一列是1,第二行右上角第2列是5,左上角第2列是1
  • 左下角矩阵,比左上角矩阵,同一行同一列大4,如左下角矩阵第二行第一列是6,左上角第二行第一列是2
  • 右下角同一行同一列,比左下角小4,如右下角第三行第二列是4,左下角第三行第二列是8

把大矩阵,一分为2,等分为4个矩阵,直到边距为1不能分为止,先依次计算边距为2的左上角、右上角、左下角、右下角,然后递归结束,这就相当于计算出边距为4的左上角,继续计算边距为4的右上角、左下角、右下角,递归结束,这就是计算出边距为8的左上角,继续...
上角、右上角、左下角、右下角,按照这个顺序,因为规律都是依赖左上角

public class SportsSchedule {

    /**
    * @description 2的n次幂支球队打球,否则不适合分治
    * @author      PangTiemin
    * @param
    * @return
    * @date        2021/5/10 13:54
    */
    public void scheduleTable(int[][]table,int n){
        if (n==1){
            table[0][0]=1;
        }else {
            int m = n/2;
            //填左上角
            scheduleTable(table,m);

            //填右上角
            //遍历行
            for (int i=0;i<m;i++){
                //遍历列,n就是当前递归矩阵的边界大小,值不一定是原n
                for (int j=m;j<n;j++){
                    table[i][j]=table[i][j-m]+m;//规律
                }
            }
            //填左下角
            for (int i=m;i<n;i++){
                for (int j=0;j<m;j++){
                    table[i][j]=table[i-m][j]+m;
                }
            }
            //填右下角
            for (int i=m;i<n;i++){
                for (int j=m;j<n;j++){
                    table[i][j]=table[i][j-m]-m;
                }
            }
        }
    }

    public static void main(String[] args) {
        SportsSchedule sportsSchedule = new SportsSchedule();
        int n=8;
        int[][] table=new int[n][n];
        sportsSchedule.scheduleTable(table,n);
        for (int i=0;i<n;i++){
            for (int j=0;j<n;j++){
                System.out.print(table[i][j]+" ");
            }
            System.out.println();
        }
    }
}

棋盘覆盖

一下图片矩阵,给定一个特殊点,比如0所在位置,第一行第二列,要求围绕特殊点,使用L型图案将棋盘布满。L型可以倒过来,左右对称,上下相反,比如我画出的3个红色L。

思路也是把大矩阵划分为小矩阵,比如边距为4矩阵划分为4个边距为2矩阵。

因为特殊点肯定会在4个中的一个,剩下的三个矩阵,最里边的点我们将它设置为同一个数字,这样保证最里面的就是一个L型,如下图。

逻辑是:分别判断特殊点是否在左上、右上、左下、右下。如果特殊点在右上角,就递归处理;如果不是,则认为右上角矩阵的左下角是右上角矩阵新的特殊点,给他一个新数字,(如上会跟另外两个矩阵组成一个L),然后将右上角递归处理!这样会让右上角矩阵作为一个新的整体矩阵继续处理。

如果特殊点在左上角、左下角、右下角,逻辑是一样的。

public class ChessBoard {

    //边距长度
    private int size;

    //左上角起始点坐标
    private int leftStartRow;
    private int leftStartCol;

    //特殊点坐标
    private int spectialRow;
    private int spectialCol;

    //L型标志内容,用数字表示
    private int type=0;

    //棋盘
    private int[][]borad;

    public ChessBoard(int size, int leftStartRow, int leftStartCol, int spectialRow, int spectialCol) {
        this.size = size;
        this.leftStartRow = leftStartRow;
        this.leftStartCol = leftStartCol;
        this.spectialRow = spectialRow;
        this.spectialCol = spectialCol;
        this.borad = new int[size][size];
    }

    private void chess(int size, int leftStartRow, int leftStartCol, int spectialRow, int spectialCol){
        //size=1不可用继续分治为小问题
        if (size==1){
            return;
        }
        //当前递归,置放 L型 的数字,以4为模是只需要3个数字,以56都可以,这样出现的数字会变多
        type = type%4+1;
        int n = type;
        //把棋盘1分为4,分治为更小的棋盘,直到边距为1
        int subSize = size/2;

        //如果特殊点在左上角矩阵
        if (spectialRow<leftStartRow+subSize&&spectialCol<leftStartCol+subSize){
            chess(subSize,leftStartRow,leftStartCol,spectialRow,spectialCol);
        }else {
            //特殊点不在左上角矩阵,就把左上角矩阵的右下角当做是新的特殊点,当前左上角矩阵的特殊点
            borad[leftStartRow+subSize-1][leftStartCol+subSize-1]=n;
            chess(subSize,leftStartRow,leftStartCol,leftStartRow+subSize-1,leftStartCol+subSize-1);
        }

        //如果特殊点在右上角
        if (spectialRow<leftStartRow+subSize&&spectialCol>=leftStartCol+subSize){
            //新矩阵起始点位置变
            chess(subSize,leftStartRow,leftStartCol+subSize,spectialRow,spectialCol);
        }else {
            //特殊点不在右上角矩阵,就把右上角矩阵的左下角当做是新的特殊点,当前右上角矩阵的特殊点
            borad[leftStartRow+subSize-1][leftStartCol+subSize]=n;
            chess(subSize,leftStartRow,leftStartCol+subSize,leftStartRow+subSize-1,leftStartCol+subSize);
        }

        //如果特殊点在左下角
        if (spectialRow>=leftStartRow+subSize&&spectialCol<leftStartCol+subSize){
            //新矩阵起始点位置变
            chess(subSize,leftStartRow+subSize,leftStartCol,spectialRow,spectialCol);
        }else {
            //右上角置为特殊点
            borad[leftStartRow+subSize][leftStartCol+subSize-1]=n;
            chess(subSize,leftStartRow+subSize,leftStartCol,leftStartRow+subSize,leftStartCol+subSize-1);
        }

        //如果特殊点在右下角
        if (spectialRow>=leftStartRow+subSize&&spectialCol>=leftStartCol+subSize){
            //新矩阵起始点位置变
            chess(subSize,leftStartRow+subSize,leftStartCol+subSize,spectialRow,spectialCol);
        }else {
            //左上角置为特殊点
            borad[leftStartRow+subSize][leftStartCol+subSize]=n;
            chess(subSize,leftStartRow+subSize,leftStartCol+subSize,leftStartRow+subSize,leftStartCol+subSize);
        }
    }

    public void chessResult(){
        borad[spectialRow][spectialCol]=this.type;
        chess(this.size,this.leftStartRow,this.leftStartCol,spectialRow,spectialCol);
        for (int i=0;i<size;i++){
            for (int j=0;j<size;j++){
                System.out.print(borad[i][j]+" ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        ChessBoard chessBoard = new ChessBoard(4,0,0,0,1);
        chessBoard.chessResult();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值