1. 程式人生 > >C語言解決棋盤覆蓋問題

C語言解決棋盤覆蓋問題

棋盤覆蓋問題是典型的利用分治法解決問題

把大問題分解成為相同性質的子問題

分治的技巧在於如何劃分棋盤,使劃分後的子棋盤的大小相同,並且每個子棋盤均包含一個特殊方格,從而將原問題分解為規模較小的棋盤覆蓋問題。k>0時,可將2^k×2^k的棋盤劃分為4個2^(k-1)×2^(k-1)的子棋盤,如圖4.11(a)所示。這樣劃分後,由於原棋盤只有一個特殊方格,所以,這4個子棋盤中只有一個子棋盤包含該特殊方格,其餘3個子棋盤中沒有特殊方格。為了將這3個沒有特殊方格的子棋盤轉化為特殊棋盤,以便採用遞迴方法求解,可以用一個L型骨牌覆蓋這3個較小棋盤的會合處

 

void chessBoard(int row, int column, int x, int y, int siz) {
    // 遞迴出口
    if(siz == 1) {
        return;
    }

    // 對半劃分成2^(siz - 1) * 2^(siz - 1)的棋盤
    int s = siz / 2;
    // L型牌編號自增
    int t = ++number;
    // 中間點,以此判別(x,y)在哪個子棋盤中
    int centerRow = row + s;
    int centerColumn = column + s;
    // 黑色方格在左上子棋盤
    if(x < centerRow && y < centerColumn) {
        chessBoard(row, column, x, y, s);
    } else {
        // 不在則填充左上子棋盤的右下角
        chess[centerRow - 1][centerColumn - 1] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(row, column, centerRow - 1, centerColumn - 1, s);
    }

    // 黑色方格在右上子棋盤
    if(x < centerRow && y >= centerColumn) {
        chessBoard(row, centerColumn, x, y, s);
    } else {
        // 不在則填充右上子棋盤的左下角
        chess[centerRow - 1][centerColumn] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(row, centerColumn, centerRow - 1, centerColumn, s);
    }

    // 黑色方格在左下子棋盤
    if(x >= centerRow && y < centerColumn) {
        chessBoard(centerRow, column, x, y, s);
    } else {
        // 不在則填充左下子棋盤的右上角
        chess[centerRow][centerColumn - 1] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(centerRow, column, centerRow, centerColumn - 1, s);
    }

    // 黑色方格在右下子棋盤
    if(x >= centerRow && y >= centerColumn) {
        chessBoard(centerRow, centerColumn, x, y, s);
    } else {
        // 不在則填充右下子棋盤的左上角
        chess[centerRow][centerColumn] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(centerRow, centerColumn, centerRow, centerColumn, s);
    }

}

解決此遞迴方程可得T( k )= O ( 4^k )

由於覆蓋一個2^k*2^k棋盤所需的L型骨牌個數為( 4^k-1 )/ 3.

這個演算法ChessBoard是一個在漸進意義下最優的演算法。

程式碼實現

棋盤覆蓋
#include<stdio.h>
const int maxNum = 1 << 10;
// 棋盤
int chess[maxNum][maxNum];
// L型牌編號
int number;
void chessBoard(int row, int column, int x, int y, int siz) {
    // 遞迴出口
    if(siz == 1) {
        return;
    }

    // 對半劃分成2^(siz - 1) * 2^(siz - 1)的棋盤
    int s = siz / 2;
    // L型牌編號自增
    int t = ++number;
    // 中間點,以此判別(x,y)在哪個子棋盤中
    int centerRow = row + s;
    int centerColumn = column + s;
    // 黑色方格在左上子棋盤
    if(x < centerRow && y < centerColumn) {
        chessBoard(row, column, x, y, s);
    } else {
        // 不在則填充左上子棋盤的右下角
        chess[centerRow - 1][centerColumn - 1] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(row, column, centerRow - 1, centerColumn - 1, s);
    }

    // 黑色方格在右上子棋盤
    if(x < centerRow && y >= centerColumn) {
        chessBoard(row, centerColumn, x, y, s);
    } else {
        // 不在則填充右上子棋盤的左下角
        chess[centerRow - 1][centerColumn] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(row, centerColumn, centerRow - 1, centerColumn, s);
    }

    // 黑色方格在左下子棋盤
    if(x >= centerRow && y < centerColumn) {
        chessBoard(centerRow, column, x, y, s);
    } else {
        // 不在則填充左下子棋盤的右上角
        chess[centerRow][centerColumn - 1] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(centerRow, column, centerRow, centerColumn - 1, s);
    }

    // 黑色方格在右下子棋盤
    if(x >= centerRow && y >= centerColumn) {
        chessBoard(centerRow, centerColumn, x, y, s);
    } else {
        // 不在則填充右下子棋盤的左上角
        chess[centerRow][centerColumn] = t;
        // 然後覆蓋其他格子,注意這時(x,y)要看做已填充位置
        chessBoard(centerRow, centerColumn, centerRow, centerColumn, s);
    }

}
int main() {
    // 大小,黑色方格位置
    int siz, x, y;
    while(true) {
        printf("(x,y)從(0,0)開始,輸入資料為0 0 0即結束程式。" );
        printf( "請輸入棋盤大小");
        scanf_s("%d",&siz);
         printf( "請輸入黑色方格位置(x,y):");
        scanf_s("%d",&x);
        scanf_s("%d",&y);
        // 退出條件
        if(siz == 0) {
            break;
        }
        // 塗黑(x,y),初始化L型牌編號
        chess[x][y] = number = 1;

        // 分治法填滿棋盤
        chessBoard(0, 0, x, y, siz);

        // 輸出該棋盤
        for(int i = 0; i < siz; i++) {
            for(int j = 0; j < siz; j++) {
               printf("%d\t" ,chess[i][j] );
            }
            printf("\n\n\n");
        }
    }

    return 0;
}

根據執行結果可以看出L型骨牌的分佈情況