<b>残缺棋盘</b> 6 V n" I9 ~ u7 p" `6 L% q<>残缺棋盘(defective chessboard)是一个有2k×2k 个方格的棋盘,其中恰有一个方格残缺。图2 - 3给出k≤2时各种可能的残缺棋盘,其中残缺的方格用阴影表示。注意当k= 0时,仅存在一种可能的残缺棋盘(如图1 4 - 3 a所示)。事实上,对于任意k,恰好存在22k 种不同的残缺棋盘。 4 t) w- [" B6 Y6 S # d2 q% W" k$ u& t+ e残缺棋盘的问题要求用三格板(t r i o m i n o e s)覆盖残缺棋盘(如图1 4 - 4所示)。在此覆盖中,两个三格板不能重叠,三格板不能覆盖残缺方格,但必须覆盖其他所有的方格。在这种限制条件下,所需要的三格板总数为( 22k -1 ) / 3。可以验证( 22k -1 ) / 3是一个整数。k 为0的残缺棋盘很容易被覆盖,因为它没有非残缺的方格,用于覆盖的三格板的数目为0。当k= 1时,正好存在3个非残缺的方格,并且这三个方格可用图1 4 - 4中的某一方向的三格板来覆盖。 5 U0 I6 w7 p2 P1 C+ [ ( f2 z0 G, n- L" i+ y" x用分而治之方法可以很好地解决残缺棋盘问题。这一方法可将覆盖2k×2k 残缺棋盘的问题转化为覆盖较小残缺棋盘的问题。2k×2k 棋盘一个很自然的划分方法就是将它划分为如图1 4 - 5 a所示的4个2k - 1×2k - 1 棋盘。注意到当完成这种划分后, 4个小棋盘中仅仅有一个棋盘存在残缺方格(因为原来的2k×2k 棋盘仅仅有一个残缺方格)。首先覆盖其中包含残缺方格的2k - 1×2k - 1 残缺棋盘,然后把剩下的3个小棋盘转变为残缺棋盘,为此将一个三格板放在由这3个小棋盘形成的角上,如图14-5b 所示,其中原2k×2k 棋盘中的残缺方格落入左上角的2k - 1×2k - 1 棋盘。可以采用这种分割技术递归地覆盖2k×2k 残缺棋盘。当棋盘的大小减为1×1时,递归过程终止。此时1×1的棋盘中仅仅包含一个方格且此方格残缺,所以无需放置三格板。 Z9 E9 D! e: M% D5 a2 ]# w1 R) {( _
可以将上述分而治之算法编写成一个递归的C++ 函数Ti l e B o a r d (见程序1 4 - 2 )。该函数定义了一个全局的二维整数数组变量B o a r d来表示棋盘。B o a r d [ 0 ] [ 0 ]表示棋盘中左上角的方格。该函数还定义了一个全局整数变量t i l e,其初始值为0。函数的输入参数如下:% p+ f, Y c+ v W2 J' s
4 b: J: M' ]; [? tr 棋盘中左上角方格所在行。 ; m* p4 h) P6 U* A- M+ L$ p6 ?) f" ~5 j7 I( a" Q, U; u
? tc 棋盘中左上角方格所在列。 7 e1 B* b! E7 O0 ?/ X* K# e. t" h' n
? dr 残缺方块所在行。 1 m% a |8 B- M! A ' C- c. ~ B+ t! o. f? dl 残缺方块所在列。5 r' D1 s3 R" S& Y' s
+ O" r4 i* o c) H7 i* l, s? size 棋盘的行数或列数。 # I. e) n( s5 L4 x- t) f* y' D8 I) H, B* a
Ti l e B o a r d函数的调用格式为Ti l e B o a r d(0,0, dr, dc,size),其中s i z e = 2k。覆盖残缺棋盘所需要的三格板数目为( s i z e2 -1 ) / 3。函数TileBoard 用整数1到( s i z e2-1 ) / 3来表示这些三格板,并用三格板的标号来标记被该三格板覆盖的非残缺方格。 2 v# \9 L/ Q$ i& g- W& @" @' T/ r5 F% I7 [' c
令t (k) 为函数Ti l e B o a r d覆盖一个2k×2k 残缺棋盘所需要的时间。当k= 0时,s i z e等于1,覆盖它将花费常数时间d。当k > 0时,将进行4次递归的函数调用,这些调用需花费的时间为4t (k-1 )。除了这些时间外, if 条件测试和覆盖3个非残缺方格也需要时间,假设用常数c 表示这些额外时间。可以得到以下递归表达式:( ?# G I6 z5 l: t* W. W
8 l, G9 T% e! ~程序14-2 覆盖残缺棋盘( q n' c9 I& K5 A5 |. Y
9 h4 D1 [3 g2 m5 n$ P% z
void TileBoard(int tr, int tc, int dr, int dc, int size) $ o% f$ Q2 D: r9 {2 _- ] " x/ R- m3 c1 k8 i/ p: l/ \$ e3 Q4 y8 x{// 覆盖残缺棋盘 + W Q( M E' K+ n+ o9 j9 O6 K5 Q! S; x7 E T5 `* G
if (size == 1) return; # j- c6 G: E0 `# ^$ d% }0 g- U: L# }! R& ?8 t
int t = tile++, // 所使用的三格板的数目 9 F( U8 ^& [( F* D ' F* T3 S& H/ I! Vs = size/2; // 象限大小0 ?6 F. d- Y8 K( [) n) ^2 K J
# u' P* p( y: P5 L: X8 d4 \if (dr < tr + s && dc < tc + s) . r5 S0 |* }- }, r, ^% P$ y3 Y# M n$ L! D q) B7 }$ l5 N* x/ I
// 残缺方格位于本象限 . E. s' ?* T9 O& S v! h; R9 J6 y* |; w" E0 c5 t& d
Ti l e B o a r d ( t r, tc, dr, dc, s);( _, q! [& b* {
( G& E$ p. O! ]. `
else {// 本象限中没有残缺方格# m: l! s) o3 G5 n+ c# S
* @/ m/ F( A7 X7 A9 A$ f, s) E4 S
// 把三格板t 放在右下角/ a# |5 {7 }6 d5 b+ R
; }8 t: Y( i) B( s( k: s8 e" lBoard[tr + s - 1][tc + s - 1] = t;! }; l" T* \0 g4 y( i5 n, P
. N5 ^ y$ K) [$ P" P// 覆盖其余部分 * d9 T* w1 B& a8 x* h) m9 k' n4 H4 u. J8 y+ j# U; L) N' d# G; {" G) W
Ti l e B o a r d ( t r, tc, tr+s-1, tc+s-1, s);}# e! Y+ x4 _8 s- U. N" E
0 M+ Y* }+ z$ j3 V; q# c* |
/ /覆盖右上象限) B N. Q5 r% Q% ?5 H
. v" E, _& {$ a! ]1 x l/ {6 c
if (dr < tr + s && dc >= tc + s)& R, P K5 B% U, a0 ^, b1 w. U$ C# ?
: Z8 ?/ M% K1 l// 残缺方格位于本象限" O6 N& Z. C3 d# b0 u
6 F7 C) w- b' q/ b9 b% n' h5 ?Ti l e B o a r d ( t r, tc+s, dr, dc, s);' y2 o3 c; w& k! s4 ^: g' k7 F b$ u
1 c0 w6 G) p, @" _" i- {" X
else {// 本象限中没有残缺方格 : |" g: k# x& Z/ C1 i% u' Y- ?* @, i & Q! A; c" G8 t5 d6 U9 _; ]// 把三格板t 放在左下角 3 L: b h% T$ H' d4 A* e. w! f5 m) x# D' o) k4 X+ a
Board[tr + s - 1][tc + s] = t; ; R. \" P( j0 B& O _" D) ?) X2 S w7 i2 o) W: `2 G
// 覆盖其余部分9 {6 @6 i0 A0 L. m2 R; u) Y+ D2 Z
& D$ J7 r, J: }) A0 {
Ti l e B o a r d ( t r, tc+s, tr+s-1, tc+s, s);} ) z/ e! b- X+ c8 H4 t& Q 0 u4 A) p9 |- W" W/ /覆盖左下象限# T% `8 h, ^# m& |+ c1 ?2 p
5 z" F- s1 y0 V( n' o
if (dr >= tr + s && dc < tc + s). b" U8 e3 z* K: a1 p
m/ _: T: C) _. v+ z$ f% \: c
// 残缺方格位于本象限" O0 e* y o u) B% G