1. 程式人生 > >資訊學奧賽輔導 殘缺棋盤——分治演算法

資訊學奧賽輔導 殘缺棋盤——分治演算法

/* 殘缺棋盤
一個有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;
}