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型骨牌的分佈情況