資訊學奧賽輔導 殘缺棋盤——分治演算法
阿新 • • 發佈:2019-01-28
/* 殘缺棋盤 一個有2k×2k個方格的棋盤,其中恰有一個方格殘缺。 在殘缺棋盤問題中,要求用三格板(triominoes)覆蓋殘缺棋盤。 在此覆蓋中,兩個三格板不能重疊。三格板不能覆蓋殘缺方格,但必須覆蓋其他所有的方格。 在這種限制條件下,所需要的三格板總數為(22k-1)/3 ,可以驗證(22k-1)/3是一個整數。 2k×2k棋盤一個很自然的劃分方法就是將它劃分為4個2k-1×2k-1棋盤。 注意到當完成這種劃分後,4個小棋盤中僅僅有一個棋盤存在殘缺方格(因為原來的棋盤僅僅有一個殘缺方格)。 對原2k×2k殘缺棋盤的覆蓋按如下兩個步驟可以轉化為對2k-1×2k-1殘缺棋盤的覆蓋: 第一步,覆蓋其中包含殘缺方格的2k-1×2k-1殘缺棋盤; 第二步:將一個三格板放在由剩下的3個小棋盤形成的角上,從而把這3個小棋盤轉變為2k-1×2k-1 殘缺棋盤, 其中原2k×2k棋盤中的殘缺方格落入左上角的2k-1×2k-1小棋盤中。 採用這種分割技術可以遞迴地覆蓋2k×2k 殘缺棋盤。當棋盤的大小減為1×1時,遞迴過程終止。 此時1×1的棋盤中僅僅包含一個方格且此方格殘缺,所以無需放置三格板。 */ #include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1<<14; //max value of k is 14, count for 2^28 -- 256M int box[maxn][maxn]; //填充區域 int cnt; //填充方塊編號 /* ** r:填充區域左頂點行 ** c:填充區域左頂點列 ** x:填充區域內殘缺點行 ** y:填充區域內殘缺點列 ** sz:填充區域寬度的指數,實際寬度用 (1<<sz)表示 */ void cover(int r, int c, int x, int y, int sz) { if(sz<1) return; cnt++; int s = 1<<sz-1; //4分之1區域的寬 if(x<r+s && y<c+s) { //左上角 box[r+s-1][c+s] = cnt; box[r+s][c+s-1] = cnt; box[r+s][c+s] = cnt; cover(r, c, x, y, sz-1); cover(r, c+s, r+s-1, c+s, sz-1); cover(r+s, c, r+s, c+s-1, sz-1); cover(r+s, c+s, r+s, c+s, sz-1); } else if(x<r+s && y>=c+s) { //右上角 box[r+s-1][c+s-1] = cnt; box[r+s][c+s-1] = cnt; box[r+s][c+s] = cnt; cover(r, c+s, x, y, sz-1); cover(r, c, r+s-1, c+s-1, sz-1); cover(r+s, c, r+s, c+s-1, sz-1); cover(r+s, c+s, r+s, c+s, sz-1); } else if(x>=r+s && y<c+s) { //左下角 box[r+s-1][c+s-1] = cnt; box[r+s-1][c+s] = cnt; box[r+s][c+s] = cnt; cover(r+s, c, x, y, sz-1); cover(r, c, r+s-1, c+s-1, sz-1); cover(r, c+s, r+s-1, c+s, sz-1); cover(r+s, c+s, r+s, c+s, sz-1); } else if(x>=r+s && y>=c+s) { //右下角 box[r+s-1][c+s-1] = cnt; box[r+s-1][c+s] = cnt; box[r+s][c+s-1] = cnt; cover(r+s, c+s, x, y, sz-1); cover(r, c, r+s-1, c+s-1, sz-1); cover(r, c+s, r+s-1, c+s, sz-1); cover(r+s, c, r+s, c+s-1, sz-1); } } /* ** sz:區域的寬度,實際寬度用 (1<<sz) 表示 */ void print_box(int sz) { int len = 1<<sz; for(int i=0; i<len; i++) { for(int j=0; j<len; j++) { printf("%3d", box[i][j]); } putchar('\n'); } } int main() { int k, kase = 0; while(~scanf("%d",&k)) { int x, y; cnt = 0; memset(box, 0, sizeof(box)); scanf("%d%d",&x,&y); cover(0, 0, x, y, k); if(kase) putchar('\n'); printf("Case #%d: \n", ++kase); print_box(k); } return 0; }