<b>残缺棋盘</b> k- |( a g3 U- o
<>残缺棋盘(defective chessboard)是一个有2k×2k 个方格的棋盘,其中恰有一个方格残缺。图2 - 3给出k≤2时各种可能的残缺棋盘,其中残缺的方格用阴影表示。注意当k= 0时,仅存在一种可能的残缺棋盘(如图1 4 - 3 a所示)。事实上,对于任意k,恰好存在22k 种不同的残缺棋盘。 : y, U g$ X8 ]' @" {0 V - m) D* _* A o6 M! N残缺棋盘的问题要求用三格板(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中的某一方向的三格板来覆盖。 {, Q# e- l' [. o% w* @& X5 a! |# E! ~4 Q* D7 n1 p
用分而治之方法可以很好地解决残缺棋盘问题。这一方法可将覆盖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的棋盘中仅仅包含一个方格且此方格残缺,所以无需放置三格板。 ) W- g$ ?; F/ F; K/ @& A( l" ]' l) F! J7 R/ |0 m) ~& p
可以将上述分而治之算法编写成一个递归的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。函数的输入参数如下:& s7 m# i: i4 @
9 ^1 ~5 k5 |+ V7 n' |. i? size 棋盘的行数或列数。7 E/ C+ ?# n" T- Z
7 C7 g: A$ W6 ?9 y3 oTi 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来表示这些三格板,并用三格板的标号来标记被该三格板覆盖的非残缺方格。$ r% h4 R* q: k
6 W% \6 P% R$ P
令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 表示这些额外时间。可以得到以下递归表达式:- c* l. ?- x' Z0 s, \
* D9 r0 R5 P: K# y: a8 A程序14-2 覆盖残缺棋盘, W$ j T8 x7 ]; M$ Y$ U
- A) s! S0 U6 T' X0 ~: ?! D d( r
void TileBoard(int tr, int tc, int dr, int dc, int size)+ N* s* V) `3 s3 L8 U; Z _
9 i1 w$ o- @6 m5 z, k5 r1 E2 I{// 覆盖残缺棋盘2 v* K9 E$ K9 W1 y2 [* ]
" n. r1 T/ ?1 B5 X8 N
if (size == 1) return;9 u4 t7 z* L/ W% A$ ]) ?: e! e. E
2 e2 c7 e* G- T1 K# G9 n( R
int t = tile++, // 所使用的三格板的数目 6 J% z. V n. x' |0 X. x* P$ c- Z- E( @4 r1 x- K
s = size/2; // 象限大小( v- U5 g! ~+ Q+ R1 _
0 {2 y7 \4 i- O
/ /覆盖左上象限8 v: ]5 B7 a% B8 S) U0 O! g
: h1 w5 ~$ \ V- J1 c, }2 u" Oif (dr < tr + s && dc < tc + s) * x- z- i! J, J0 w; {* D7 @# j5 @7 D. l! o9 X( M
// 残缺方格位于本象限' X, X- m! G0 x! U, O
/ E3 S& D; c5 [
Ti l e B o a r d ( t r, tc, dr, dc, s); : U/ m) {- O8 n I: u " D$ O o, @% `% R2 B* `8 Helse {// 本象限中没有残缺方格/ ~8 D% m+ W5 S2 f& l
1 H/ A) \, i% C/ i/ J' E- |
// 把三格板t 放在右下角 $ X6 h' a+ `1 G" N4 @& o& E% l" A. F) w, g( e, `) E* I Z/ B
Board[tr + s - 1][tc + s - 1] = t;4 T' }5 E) R4 M* b) h
+ k: s9 {8 n; U) u1 X! _// 覆盖其余部分 " ]0 B& F o- i& K/ V# D8 f' B: u/ R6 U6 N) B5 R
Ti l e B o a r d ( t r, tc, tr+s-1, tc+s-1, s);} 0 ]( d8 x+ \$ P8 o; _0 L) c6 t3 Q0 s% s) x: i, m
/ /覆盖右上象限4 i3 o8 O1 [/ M F" O
) r, g# k3 s3 T/ {8 b9 t( e# v! z3 u
if (dr < tr + s && dc >= tc + s) , D3 g2 D5 w! D) x1 x0 ]( x0 ~9 G+ g3 h
// 残缺方格位于本象限 & k0 m% C3 N" A4 [; h " H! |' g6 s. Q* N3 [$ WTi l e B o a r d ( t r, tc+s, dr, dc, s); 0 L& w* K# u: r* l9 v1 y 6 g" i2 d5 y$ Helse {// 本象限中没有残缺方格 . C1 W2 _5 @' e# q" C U0 h 2 _' g# I& v' T5 j// 把三格板t 放在左下角$ a- a/ S' L1 V2 ^" B8 D
3 K9 W5 w1 M3 z) C9 LBoard[tr + s - 1][tc + s] = t; W" m7 M2 t' n: @
4 l# R4 H& A, z n. t
// 覆盖其余部分. w4 ]- ~2 p" \/ K; \
! t5 N6 S+ d9 K+ q
Ti l e B o a r d ( t r, tc+s, tr+s-1, tc+s, s);}# M U* M3 h' `4 {" w' j# ?+ ~
( z% u, h2 f+ d& ~. n$ W/ /覆盖左下象限 + A% L9 A! H* b: N/ j% f( k6 |5 V ( j( N3 \% l" x6 iif (dr >= tr + s && dc < tc + s): O* E' \) X3 t3 m# p: Z
5 G5 R% ] y* J& U$ I" X
// 残缺方格位于本象限 0 X9 O' K/ p$ N# N 1 C E" C; R6 v; A' c# ITileBoard(tr+s, tc, dr, dc, s); . A& m. A) @2 ~4 X3 r* n 6 ~% k7 D+ v. C9 pelse {// 把三格板t 放在右上角 `3 O! f6 @% x9 t9 \ + w; m; X% r8 }/ G; V" iBoard[tr + s][tc + s - 1] = t;9 s: u* F9 a f5 y# b/ R, o& \
* \ D, V; [' h// 覆盖其余部分 $ C' ^/ N# r* l! V 5 `" F7 _9 @/ L7 z/ i8 t6 E* gTileBoard(tr+s, tc, tr+s, tc+s-1, s);} 2 J2 n! p# Y" L- S2 J9 k: `: W s; V
// 覆盖右下象限5 _( z% r# O: r$ P1 L4 K
3 v6 Q: N, P/ \) j* P6 i# W
if (dr >= tr + s && dc >= tc + s)( _# [& L+ i- G7 A! \, Z- F- q
+ s, C! u) t" p0 G4 [' c2 B
// 残缺方格位于本象限, m- N% v( P! Y* ? \
, K7 h. E4 L# x8 {5 X
TileBoard(tr+s, tc+s, dr, dc, s); " |6 {* e' K) v' `' e4 O- S5 w& w7 D
else {// 把三格板t 放在左上角 # T" w$ A; U; ?( T8 a( A, H; B+ Q% }: {" N3 J1 i
Board[tr + s][tc + s] = t;7 I) {7 y0 Z/ e
- ~& Y' r. i0 s" @
// 覆盖其余部分 - {5 i f9 x9 c 5 |. Z; O% R6 `" |TileBoard(tr+s, tc+s, tr+s, tc+s, s);}% M' c; k3 H( J& [, c; F2 l* q
% s( Q/ ^/ G) \: [8 O1 ~( ^
} 4 J/ x" y5 K ]+ v 2 V9 S- i% F3 Lvoid OutputBoard(int size)2 M; U; r' S9 J0 z9 k! s
0 N9 S/ T: W0 j) G0 @& N4 k0 f{$ ~$ A: ^1 t I2 ^+ X
& k2 Z; q. w$ m( s% b& v
for (int i = 0; i < size; i++) {) m& {) j ~/ E" E$ R; D
; v* ]# M! Z' J% e3 P
for (int j = 0; j < size; j++)0 v: G, {; x5 C6 T. N% n# L
- i k$ Z' v( h% _) N0 ~! J# ~; [cout << setw (5) << Board[j]; ) U5 f% p# |4 C! L9 p& z& Y* K$ o7 Z4 d' X
cout << endl; & A( |( E, R O+ N* l! Q % k3 E& \! [/ Z% f" O/ U1 }}8 F, H. s6 e9 f
: O I+ ]3 Q. C* r) a6 u7 l}7 `9 `# j# }' t
$ D9 ^# ^) i R% Y
可以用迭代的方法来计算这个表达式(见例2 - 2 0),可得t (k )= ( 4k )= (所需的三格板的数目)。由于必须花费至少( 1 )的时间来放置每一块三格表,因此不可能得到一个比分而治之算法更快的算法。</P>