棋盘覆盖

一、问题描述

在一个 2k * 2k(k ≥ 0)个方格组成的棋盘中,恰有一个方格与其他方格不同,称该方格为特殊方格,显然,特殊方格在棋盘中出现的位置有 4k 种情形,因而有 4k 种不同的棋盘。

image  image

二、算分分析

用分治策略。

  1. 当 k > 0 时,将 2k * 2k 棋盘分割成 4 个 2k-1 * 2k-1 子棋盘,特殊方格必定只位于某个子棋盘中。
  2. 用一个 L 型骨牌覆盖 3 个无特殊方格子棋盘的结合处,由原问题转化成 4 个较小规模的棋盘覆盖子问题。

imageimage

image

image  image

image  image

三、代码实现

#include <stdio.h>

#define max 1024

int board[max][max]; // 最大棋盘
static int tile = 0; // 覆盖标志位

/**
  *  (tr,tc)  棋盘左上角的方格坐标
  *  (dr,dc) 特殊方格所在的坐标
  *    size   是棋盘的行数和列数
  */
void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
    if(size == 1) return; // 递归到棋盘大小为 1 时,则结束递归
    
    int t = tile++;   // L型骨牌号
    int s = size / 2; // 分割棋盘,使得新得到的棋盘为原来棋盘大小的四分之一
   
    // ①、左上角子棋盘
    // 如果特殊方格在左上角,就对这个棋盘左上角的四分之一重新进行棋盘覆盖
    if(dr < tr + s && dc < tc + s) {
        // 特殊方格在此棋盘中
        ChessBoard(tr, tc, dr, dc, s);
    }
    // 因为特殊方格不在左上角,所以要在左上角构造一个特殊方格
    else {
        // 用 t 号 L 型骨牌覆盖右下角
        board[tr + s - 1][ tc + s - 1] = t;
        // 在构造完特殊方格之后,棋盘的左上角的四分之一又有了特殊方格,所以就对左上角棋盘的四分之一进行棋盘覆盖,直到棋盘大小为 1 * 1
        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 {
        // 用 t 号 L 型骨牌覆盖右上角
        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 {
        
        // 用 t 号 L 型骨牌覆盖左上角
        board[tr + s][tc + s] = t;
        ChessBoard(tr + s, tc + s, tr + s, tc + s, s);
    }
}

int main()
{
    int size = 8, dr = 0, dc = 0;
    
    printf("不可覆盖点的值为 -1!\n\n");
    board[dr][dc] = -1;
    
    ChessBoard(0, 0, dr, dc, size);
    
    for(int m = 0; m < size; m++)
    {
        printf("%2d", board[m][0]);
        
        for(int n = 1; n < size; n++) {
            printf("  %2d", board[m][n]);
        }
        printf("\n");
    }
    
    return 0;
}


不可覆盖点的值为 -1!

-1   2   3   3   7   7   8   8
 2   2   1   3   7   6   6   8
 4   1   1   5   9   9   6  10
 4   4   5   5   0   9  10  10
12  12  13   0   0  17  18  18
12  11  13  13  17  17  16  18
14  11  11  15  19  16  16  20
14  14  15  15  19  19  20  20

四、内容来源

棋盘覆盖问题
分治法之棋盘覆盖问题

posted @ 2020-03-15 13:54  和风细羽  阅读(420)  评论(0编辑  收藏  举报