棋盘覆盖(二)(转)
转自:http://blog.chinaunix.net/uid-26548237-id-3505163.html
问题描述:
在一个2^k×2^k个方格组成的棋盘中,若有一个方格与其他方格不同,则称该方格为一特殊方格,且称该棋盘为一个特殊棋盘.显然特殊方格在棋盘上出现的位置有4^k种情形.因而对任何k≥0,有4^k种不同的特殊棋盘.
下图–图(1)中的特殊棋盘是当k=3时16个特殊棋盘中的一个:
图(1)
题目要求在棋盘覆盖问题中,要用下图-图(2)所示的4种不同形态的L型骨牌覆盖一个给定的特殊棋盘上除特殊方格以外的所有方格,且任何2个L型骨牌不得重叠覆盖.
图(2)
题目包含多组测试数据,输入包含测试数据组数N,下面输入N组数据,每组数据,包括边长m和特殊方格的位置x,y。
input sample
2
2
0 0
8
2 2
output sample
CASE:1
0 1
1 1
CASE:2
3 3 4 4 8 8 9 9
3 2 2 4 8 7 7 9
5 2 0 6 10 10 7 11
5 5 6 6 1 10 11 11
13 13 14 1 1 18 19 19
13 12 14 14 18 18 17 19
15 12 12 16 20 17 17 21
15 15 16 16 20 20 21 21
一)原理介绍
在一个2^k * 2^k个方格组成的棋盘中,恰有一个方格与其它方格不同,称该方格为一特殊方格,且称该棋盘为以特殊棋盘。
在棋盘覆盖问题中,要用图示的4种不同形态的L型骨牌覆盖给定的特殊棋盘上除特殊方格之外的所有方格,且任何2个L型骨牌不得重叠覆盖。
当k>0时,将2^k * 2^k棋盘分割为4个2^(k-1) * 2^(k - 1)子棋盘,如下图所示。特殊方格必位于4个较小子棋盘之一种,其余3个子棋盘中无特殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘,可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如下图所示。从而将原问题转化为4个较小规模的棋盘覆盖问题。递归地使用这种分割,直至棋盘简化为棋盘1*1。
为了更加清楚了解这个原理,下面引用:http://www.ezloo.com/2008/04/chessboard_cover.html
这里我们用分治法解决该问题。分治法是把一个规模很大的问题分解为多个规模较小的类似的子问题,然后递归地解决所有子问题,最后再由子问题的解决得到原问题的解决。
【解题思路】
将2^k * 2^k的棋盘,先分成相等的四块子棋盘,其中特殊方格位于四个中的一个,构造剩下没特殊方格的三个字棋盘,将它们中的也假设一个方格为特殊方格。如果是:
左上角的子棋盘(若不存在特殊方格):则将该子棋盘右下角的那个方格假设为特殊方格;
右上角的子棋盘(若不存在特殊方格):则将该子棋盘左下角的那个方格假设为特殊方格;
左下角的子棋盘(若不存在特殊方格):则将该子棋盘右上角的那个方格假设为特殊方格;
右下角的子棋盘(若不存在特殊方格):则将该子棋盘左上角的那个方格假设为特殊方格;
当然,上面四种情况,只可能且必定只有三种成立,那三个假设的特殊方格刚好构成一个L型骨牌,我们可以给它们作上相同的标志。这样四个子棋盘就分别都和原来的大棋盘类似,我们就可以用递归的算法解决了。
#include <stdio.h> #include <stdlib.h> #define SIZE 4 static int title = 1; //title表示L型骨牌的编号 static int board[SIZE][SIZE]; /** * 功能:棋盘覆盖 * @param tr表示棋盘左上角行号 * @param tc表示棋盘左上角列号 * @param dr表示特殊棋盘的行号 * @param dc表示特殊棋盘的列号 * @param size = 2^k * 棋盘的规格为2^k * 2^k **/ void ChessBoard(int tr, int tc, int dr, int dc, int size) { if(1 == size) { return; } int t = title++; //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; //覆盖其余方格 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 { //此子棋盘中无特殊方格 //用t号L型骨牌覆盖左下角 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); } } //打印 void ChessPrint() { int i; int j; for(i = 0; i < SIZE; i++) { for(j = 0; j < SIZE; j++) { printf("%d ", board[i][j]); } printf("n"); } } int main(int argc, char **argv) { //方便测试,假设特殊方格位置在第三行第三列 ChessBoard(0, 0, 2, 2, SIZE); ChessPrint(); return 0; }