迭代法也称辗转法,是一种不断用变量的旧值递推新值的过程,跟迭代法相对应的是直接法(或者称为一次解法),即一次性解决问题。迭代法又分为精确迭代和近似迭代。“二分法”和“牛顿迭代法”属于近似迭代法。: F% C* Y: M O
8 |& ]# e& s. W. B8 U 迭代算法是用计算机解决问题的一种基本方法。它利用计算机运算速度快、适合做重复性操作的特点,让计算机对一组指令(或一定步骤)进行重复执行,在每次执行这组指令(或这些步骤)时,都从变量的原值推出它的一个新值。 1 w# X3 u$ g- g' }/ j5 \/ B 6 {4 r$ j; P% I# G. T 利用迭代算法解决问题,需要做好以下三个方面的工作: $ o1 m8 j z' q$ W; Y5 M 3 D; d% [; F) r" @ 一、确定迭代变量。在可以用迭代算法解决的问题中,至少存在一个直接或间接地不断由旧值递推出新值的变量,这个变量就是迭代变量。 0 Z/ f4 m' y4 ]$ B, V; R/ ?( O. q6 F* Q( K
二、建立迭代关系式。所谓迭代关系式,指如何从变量的前一个值推出其下一个值的公式(或关系)。迭代关系式的建立是解决迭代问题的关键,通常可以使用递推或倒推的方法来完成。 7 ^1 V5 Z* Y" N 0 P( w% ~& }3 q: t2 Z8 ^# z5 ~6 ~& X2 S 三、对迭代过程进行控制。在什么时候结束迭代过程?这是编写迭代程序必须考虑的问题。不能让迭代过程无休止地重复执行下去。迭代过程的控制通常可分为两种情况:一种是所需的迭代次数是个确定的值,可以计算出来;另一种是所需的迭代次数无法确定。对于前一种情况,可以构建一个固定次数的循环来实现对迭代过程的控制;对于后一种情况,需要进一步分析出用来结束迭代过程的条件。 4 r: h8 S& U) O/ i8 k' h3 t, ^
/ w; I4 [* I) A' f4 C* K 例 1 : 一个饲养场引进一只刚出生的新品种兔子,这种兔子从出生的下一个月开始,每月新生一只兔子,新生的兔子也如此繁殖。如果所有的兔子都不死去,问到第 12 个月时,该饲养场共有兔子多少只? : L3 Z: `# D0 w/ c * x, S- V4 f7 P' Z5 O 分析: 这是一个典型的递推问题。我们不妨假设第 1 个月时兔子的只数为 u 1 ,第 2 个月时兔子的只数为 u 2 ,第 3 个月时兔子的只数为 u 3 ,……根据题意,“这种兔子从出生的下一个月开始,每月新生一只兔子”,则有 # ^0 \4 Y; \1 C0 o- `
2 ?( W$ o8 `6 r1 g9 i u 1 = 1 , u 2 = u 1 + u 1 × 1 = 2 , u 3 = u 2 + u 2 × 1 = 4 ,…… ; G: S+ @; e/ }+ c5 x
6 ~. Y) b3 [5 ]# H$ c- U 根据这个规律,可以归纳出下面的递推公式: - D5 a) r0 E6 t$ m3 E2 R 7 o) d- w8 {- Y& T u n = u n - 1 × 2 (n ≥ 2) : w4 o- M( q) K3 g, i P6 N2 `9 C: h, {# t) _
对应 u n 和 u n - 1 ,定义两个迭代变量 y 和 x ,可将上面的递推公式转换成如下迭代关系: ' O U, Y' `: j( X! a
! B* M4 m/ o4 H9 [2 y y=x*2 & j1 ~$ _3 z" N4 ?
- J# q) \9 `5 v) C1 _$ N: @# v( v x=y - G+ H$ ~5 m$ B( G$ x7 _/ x2 `# ]) h2 V( A4 K
让计算机对这个迭代关系重复执行 11 次,就可以算出第 12 个月时的兔子数。参考程序如下: " {' x' l/ V* A. v2 m# k0 M
% h' m: G% }3 g2 N8 Y
cls " u0 r8 M1 ?- U6 x! B - ]6 o9 ^! b, }; E* N* \. O x=1 ( V1 h, L3 y3 o: A 1 j' `1 h8 A1 i, O% H7 C5 l* c for i=2 to 12 9 }& \5 S: }) ~
% V9 m7 |5 B; O& h
y=x*2 - m: a( y2 @1 Y! H& M# q/ v5 j+ b$ {( F, k. C9 L, u b2 A
x=y 0 n3 j. a. s* ~4 f- L9 j J + G @% W$ o6 N" a8 W) o next i 1 b; U* T' R, j2 X! b. @. Q
5 T3 ~( I' Y$ U7 }- M0 ~" R print y / `6 X* ?4 t8 L1 @
. ]$ t v6 a/ h! N: T. d% y6 \
end - F6 i; Y. a i1 N
1 `* `, H; x* c
例 2 : 阿米巴用简单分裂的方式繁殖,它每分裂一次要用 3 分钟。将若干个阿米巴放在一个盛满营养参液的容器内, 45 分钟后容器内充满了阿米巴。已知容器最多可以装阿米巴 220,220个。试问,开始的时候往容器内放了多少个阿米巴?请编程序算出。 ) X( T- _7 J, k; U/ Q3 J$ ]& P
) V9 k K' F8 `7 l+ k
分析: 根据题意,阿米巴每 3 分钟分裂一次,那么从开始的时候将阿米巴放入容器里面,到 45 分钟后充满容器,需要分裂 45/3=15 次。而“容器最多可以装阿米巴2^ 20 个”,即阿米巴分裂 15 次以后得到的个数是 2^20 。题目要求我们计算分裂之前的阿米巴数,不妨使用倒推的方法,从第 15 次分裂之后的 2^20 个,倒推出第 15 次分裂之前(即第 14 次分裂之后)的个数,再进一步倒推出第 13 次分裂之后、第 12 次分裂之后、……第 1 次分裂之前的个数。 % y- m- `/ A8 S, h2 q, _# e0 E/ k8 o. q: F# W- v
设第 1 次分裂之前的个数为 x 0 、第 1 次分裂之后的个数为 x 1 、第 2 次分裂之后的个数为 x 2 、……第 15 次分裂之后的个数为 x 15 ,则有 2 W7 P3 b; b7 P. `
( t6 e( |: ~# O+ ]0 `8 m
x 14 =x 15 /2 、 x 13 =x 14 /2 、…… x n-1 =x n /2 (n ≥ 1) 3 o- W6 A, p* q2 G- ^, t
; _$ O( ]' e2 s1 s 因为第 15 次分裂之后的个数 x 15 是已知的,如果定义迭代变量为 x ,则可以将上面的倒推公式转换成如下的迭代公式: m$ Y& Y, x# q" i. x7 ^) o
5 G6 o: K7 {' H! u# y 例 3 : 验证谷角猜想。日本数学家谷角静夫在研究自然数时发现了一个奇怪现象:对于任意一个自然数 n ,若 n 为偶数,则将其除以 2 ;若 n 为奇数,则将其乘以 3 ,然后再加 1 。如此经过有限次运算后,总可以得到自然数 1 。人们把谷角静夫的这一发现叫做“谷角猜想”。 2 u* _9 s; S, l3 b! | & L5 J( M3 \# | 要求:编写一个程序,由键盘输入一个自然数 n ,把 n 经过有限次运算后,最终变成自然数 1 的全过程打印出来。 # h, `# I5 X4 Q s " Y% x# N2 \' d) M. B( I( Y 分析: 定义迭代变量为 n ,按照谷角猜想的内容,可以得到两种情况下的迭代关系式:当 n 为偶数时, n=n/2 ;当 n 为奇数时, n=n*3+1 。用 QBASIC 语言把它描述出来就是: : |0 _' B' C) b0 F: L: t, `& i1 x( O5 B1 G
if n 为偶数 then 5 b `! u) F7 T- L4 G 4 d8 }% R- p" V$ M: g n=n/2 . V# r0 ^" O& R8 `% K 6 g' {; a3 I' r5 T/ O* F7 q else 7 L" |. U( ^2 m/ R
5 R" L" H5 @8 F" R
n=n*3+1 " I6 @* b t# H+ F0 ?+ E1 g
8 y' l& A) y6 q end if 5 f) t1 m. {5 T. O( p/ r" q
0 N8 i' _; v( y+ w- M$ J4 n 这就是需要计算机重复执行的迭代过程。这个迭代过程需要重复执行多少次,才能使迭代变量 n 最终变成自然数 1 ,这是我们无法计算出来的。因此,还需进一步确定用来结束迭代过程的条件。仔细分析题目要求,不难看出,对任意给定的一个自然数 n ,只要经过有限次运算后,能够得到自然数 1 ,就已经完成了验证工作。因此,用来结束迭代过程的条件可以定义为: n=1 。参考程序如下: ! w |* a$ |- H) P6 N8 {" |( z: v" V
# a. c) n& `& Q- a0 a
cls % G7 [& I8 y T, [
8 g( L" D# h- K: l% @* u
input "Please input n=";n 5 [% w7 P0 Q& Y, a/ o- F
$ E0 l' Q9 Z Q" R2 Y' U6 T. J+ P
do until n=1 ; @. U$ v$ A0 h# v- l) z3 M) z4 e. `0 W4 t6 j
if n mod 2=0 then ! I, ^4 f6 y; B, g+ x6 ?7 t' V
% U8 K$ L p2 ?+ U rem 如果 n 为偶数,则调用迭代公式 n=n/2 " y" A5 F4 Y) L9 l2 b5 C; t
2 x7 C! ]9 D- R! J `$ b. s5 w/ p
n=n/2 2 }1 H1 y2 ]6 w' Y" G1 D9 L# }6 }1 Y7 l) `8 Z' G) h
print "—";n; ) k6 X; m3 M/ @3 `6 Y
) E- d: x0 J$ Y9 I$ h9 J0 i
else 6 F' R0 s' N; d! k! y; W. ]( U. H+ X! R# \( {3 n$ ^: e9 r1 }& H
n=n*3+1 $ N' @0 P v6 C$ o3 u
+ l5 p' G. a+ h% t* O! p print "—";n; ( t" y* Y' |: Q! M# _8 J: n/ c" R3 b: ?: U% y0 A* M5 _
end if ! E' j6 l2 d2 i5 L, F6 R2 y. f* D# ]' u
loop 2 r$ n; x; g2 v1 V9 E2 o6 S2 n' G& U+ C. B. d. o0 d
end 1 y$ z ^$ q5 T# D5 E+ J1 y$ ?1 o7 Q* Q" C' Y8 d+ n. N* f
迭代法 4 l0 r9 Z! n3 e4 \( P% m
d5 ~& i& P6 O b (1) 选一个方程的近似根,赋给变量x0; 5 e2 l: Q! t' ]) k# C1 @ 4 g6 D# t' [8 e+ y- z3 ^+ Z (2) 将x0的值保存于变量x1,然后计算g(x1),并将结果存于变量x0; 5 b6 ~: s) A: r0 m) m2 n8 c1 V7 Z+ x* ]; \5 u3 x; G
(3) 当x0与x1的差的绝对值还小于指定的精度要求时,重复步骤(2)的计算。 # I3 u* F; f/ B# Q- S1 L" x( F) y# x8 f, S2 a6 u- i
若方程有根,并且用上述方法计算出来的近似根序列收敛,则按上述方法求得的x0就认为是方程的根。上述算法用C程序的形式表示为: 5 Q, i% s- N( W& b' o' d
: g7 G* ^' h; Y# C8 g 【算法】迭代法求方程的根 8 r# l5 g# @* R8 l- V: P& u/ ?
0 E' B- S% o( x7 a: b- V, l, ]
{ x0=初始近似根; : I* y4 D5 C2 F* K6 s$ Z& l- b, a, @1 y$ s; x
do { & g4 y; R( z: `% s& M! V0 F( d # h* J( i* d# z G2 _ x1=x0; 4 k2 x/ V, N, M6 H% O1 A0 g
+ x1 n6 g$ N. m. n x0=g(x1); /*按特定的方程计算新的近似根*/ ( |9 {% [- S# M2 Z) @
0 @' S w/ p- j9 W7 l' x# C
} while ( fabs(x0-x1)>Epsilon); 7 D: @" w7 H- \1 V3 f) {( d! N
' Z0 R; a$ W O: z
printf(“方程的近似根是%f\n”,x0); 5 R: j E/ X3 L( R* L
- W" S8 }9 V2 U3 r0 O } ) H7 J2 I' L2 Q/ ^* n8 A$ k9 z - m9 o9 @8 z5 t d4 m# a5 [% U 迭代算法也常用于求方程组的根,令 $ n$ |% [) w. w1 v; `
) _0 A/ Q, ~0 l( x4 P- V# l
X=(x0,x1,…,xn-1) # L3 l' Y1 G Q N9 | 4 V1 s3 u/ r4 D: H 设方程组为: 3 W7 N; V4 r, F" m, W, k, q6 n/ u# ~9 z& H
xi=gi(X) (I=0,1,…,n-1) Q# q q, C" ^, N" g+ I8 O
1 ]% [) g3 Y" ?3 u! E7 p7 H# _
则求方程组根的迭代算法可描述如下: ! E2 P" R* G9 T1 J0 ?* B
2 L7 B5 O( P) O2 |* g& W
【算法】迭代法求方程组的根 ; H& e" P, K9 `+ k 5 ]; Q H0 ?; G6 d7 | { for (i=0;i ( v+ H% ]- U- Y" C/ M/ B
/ S* p m- Z3 X+ k. z4 D: n x=初始近似根; 2 }8 N, n& V/ o# Z) x+ r
. I! Y* K3 a' w' R; T- y3 C do { 8 h2 H8 ^4 d; W. K, _5 n) ` , Y( M p" b# d for (i=0;i / b- i2 v7 K3 G- }1 Q' Q
$ ` W+ s2 k$ ^0 A4 U
y=x; - Q6 X) J, Y1 l$ Y" T# G3 D# J* U
$ P- D: }* p, J! Y4 I f0 m for (i=0;i ; B. o9 p6 S; L1 d Y. g ' G- R! O$ e5 P x=gi(X); 5 u/ Q# R h+ D; w' \) \2 U! Z
- D: }& @; p: g0 v' C4 E
for (delta=0.0,i=0;i - B' \- y6 z' a2 _' ~. L: H 5 z0 U4 k' {! R& ] if (fabs(y-x)>delta) delta=fabs(y-x); : F: _5 E" J1 k* Y* ?; T
1 M% d+ s$ t0 U+ w* H Z: d/ L } while (delta>Epsilon); 8 E$ f! k( W8 ^) l3 X3 l+ [) c
' L! x( o5 N* R# {/ r for (i=0;i ! N1 A" S& j8 k2 z- ~4 h6 ^4 h+ X
printf(“变量x[%d]的近似根是 %f”,I,x); 4 J6 f, X4 z) J. o + b" L" q: t8 _2 r( k3 Z printf(“\n”); 8 I1 K: O N; w5 U" s
1 U! C4 e8 p! b, R } 6 N- R' f& v2 E V8 ^' m1 t' S) t
$ Q# X* A6 u% b1 t2 A( E7 ] F
具体使用迭代法求根时应注意以下两种可能发生的情况: 1 J" R, h4 v: Z
! i( ], {1 z/ `* a
(1) 如果方程无解,算法求出的近似根序列就不会收敛,迭代过程会变成死循环,因此在使用迭代算法前应先考察方程是否有解,并在程序中对迭代的次数给予限制; ) u6 ~. c1 U) r/ o; x1 K) ] Y# \7 k; D. K8 U: ]1 W
(2) 方程虽然有解,但迭代公式选择不当,或迭代的初始近似根选择不合理,也会导致迭代失败。 % y9 z! _! C+ H, w* {, i9 l. F" @: S! p: A/ |. Z
递归 : P. h9 [' ^4 h2 f5 @ 0 |: J0 X( u6 j" V& C 递归是设计和描述算法的一种有力的工具,由于它在复杂算法的描述中被经常采用,为此在进一步介绍其他算法设计方法之前先讨论它。 , X) X! b) A2 i2 O9 n : k$ ~3 `# J5 s: N' g% A! s1 A 能采用递归描述的算法通常有这样的特征:为求解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法,分解成规模更小的问题,并从这些更小问题的解构造出规模较大问题的解。特别地,当规模N=1时,能直接得解。 - a1 {5 e1 l. W' S ~. M# g9 c2 b1 e
【问题】 编写计算斐波那契(Fibonacci)数列的第n项函数fib(n)。 ! k" L# g# F3 [* h6 v6 X( T3 g& p m$ G
斐波那契数列为:0、1、1、2、3、……,即: 4 D, c5 V' D! v) P- V6 S9 w0 V0 A/ U4 p
fib(0)=0; . T$ w1 C) ~& T/ M Z
- G: X$ p# F; W2 ]1 H& f fib(1)=1; % x: B# V: M4 m) M
' W2 u4 O. }& L" S4 {
fib(n)=fib(n-1)+fib(n-2) (当n>1时)。 ' p% p' q E4 ?
$ F6 R, ~; t6 @4 R$ D
写成递归函数有: ; a8 {# J1 |- C
% w$ t; u1 i1 N9 `9 a8 f7 [2 b! e
int fib(int n) ' V2 l) x8 b; T: {6 l
, d$ Y& T- |! y. Z- c3 _ { if (n==0) return 0; " T1 @* m+ e% D9 B( ]% E: R; z8 @% S; s7 x, g. h1 S$ j
if (n==1) return 1; ' F3 x+ f$ |* e7 F( @" s
% I7 Z+ P% P0 ]) ^5 b+ b% l
if (n>1) return fib(n-1)+fib(n-2); # `3 ]9 _, O! F6 s. d0 h4 H# a
8 v1 m- }- q' G: f8 O0 ?
} $ H1 l- i; F4 l# c 6 H. v' m3 r) h* @2 i% M 递归算法的执行过程分递推和回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。例如上例中,求解fib(n),把它推到求解fib(n-1)和fib(n-2)。也就是说,为计算fib(n),必须先计算fib(n-1)和fib(n- 2),而计算fib(n-1)和fib(n-2),又必须先计算fib(n-3)和fib(n-4)。依次类推,直至计算fib(1)和fib(0),分别能立即得到结果1和0。在递推阶段,必须要有终止递归的情况。例如在函数fib中,当n为1和0的情况。 9 U1 j& C2 _) I' t/ |8 W $ o. i( l2 c$ M9 {+ } 在回归阶段,当获得最简单情况的解后,逐级返回,依次得到稍复杂问题的解,例如得到fib(1)和fib(0)后,返回得到fib(2)的结果,……,在得到了fib(n-1)和fib(n-2)的结果后,返回得到fib(n)的结果。 5 ~" s# k" N/ R8 _: M3 F - O- z+ j6 Y% w! q. q; |. H4 j 在编写递归函数时要注意,函数中的局部变量和参数知识局限于当前调用层,当递推进入“简单问题”层时,原来层次上的参数和局部变量便被隐蔽起来。在一系列“简单问题”层,它们各有自己的参数和局部变量。 : ?" U4 F0 ?/ q2 w$ |. G
" K# K/ s9 S5 S) r5 q& f, |
由于递归引起一系列的函数调用,并且可能会有一系列的重复计算,递归算法的执行效率相对较低。当某个递归算法能较方便地转换成递推算法时,通常按递推算法编写程序。例如上例计算斐波那契数列的第n项的函数fib(n)应采用递推算法,即从斐波那契数列的前两项出发,逐次由前两项计算出下一项,直至计算出要求的第n项。 7 {+ d+ N U8 b* g( m- ^ 8 q6 k5 O# j% X3 O+ j2 w 【问题】 组合问题 7 D0 ~- a. h% E( L9 j, ]( J6 V7 E5 e7 i" } e& h
问题描述:找出从自然数1、2、……、n中任取r个数的所有组合。例如n=5,r=3的所有组合为: (1)5、4、3 (2)5、4、2 (3)5、4、1 8 P5 a0 S* C* A . I+ ?: J* S- F7 L8 G) O H (4)5、3、2 (5)5、3、1 (6)5、2、1 . n" u+ ~6 H3 g$ q) ~) C/ O% E
+ U0 u& D& e8 i6 O% B2 w/ |; p (7)4、3、2 (8)4、3、1 (9)4、2、1 $ F& o, F, S! j1 _( T$ {" m2 u 5 u- p' B: H3 {8 y& K G (10)3、2、1 2 e% A" R6 x/ q% X! i+ O$ h* R
' B+ H0 a/ u- G9 F8 s/ Z% t
分析所列的10个组合,可以采用这样的递归思想来考虑求组合函数的算法。设函数为void comb(int m,int k)为找出从自然数1、2、……、m中任取k个数的所有组合。当组合的第一个数字选定时,其后的数字是从余下的m-1个数中取k-1数的组合。这就将求m 个数中取k个数的组合问题转化成求m-1个数中取k-1个数的组合问题。设函数引入工作数组a[ ]存放求出的组合的数字,约定函数将确定的k个数字组合的第一个数字放在a[k]中,当一个组合求出后,才将a[ ]中的一个组合输出。第一个数可以是m、m-1、……、k,函数将确定组合的第一个数字放入数组后,有两种可能的选择,因还未去顶组合的其余元素,继续递归去确定;或因已确定了组合的全部元素,输出这个组合。细节见以下程序中的函数comb。 3 `8 [1 K5 d8 X. z8 @5 j5 J. I! N+ K5 P$ c
【程序】 ) W0 q) Y1 Q& Q9 {1 o) s! X% z c& c; r6 t6 R5 ? O# S2 B. w( p+ G # include ) J2 U2 j0 r3 V& T! P4 w - }' J9 b$ ?" l4 F( F/ D5 j # define MAXN 100 , n6 J8 R z2 g% w% V. H ) i# g' B, Y" y int a[MAXN]; : r, \' U" k/ M$ } c$ y) M* {
& {8 C' [/ r# q' h5 R2 W1 L void comb(int m,int k) ( `+ Q, `. K. D1 [2 ]6 y$ f. h% ^: {! o A
{ int i,j; / n' l' g4 e. v+ }
$ S! g( E4 Y) K4 R- ]! G& s& ]
for (i=m;i>=k;i--) : V1 G y% b/ F2 z' j
+ C6 l$ B: M" k/ ?; W5 c { a[k]=i; $ p# v. }3 ^" [- T: i/ N4 a( ^: U
* N9 S$ O1 y* b( N) ?3 `
if (k>1) 7 }" w7 v* ^3 G8 ]; l7 y$ n
3 _9 u6 N3 P* i- e( P X7 S8 s 设n 件物品的重量分别为w0、w1、…、wn-1,物品的价值分别为v0、v1、…、vn-1。采用递归寻找物品的选择方案。设前面已有了多种选择的方案,并保留了其中总价值最大的方案于数组option[ ],该方案的总价值存于变量maxv。当前正在考察新方案,其物品选择情况保存于数组cop[ ]。假定当前方案已考虑了前i-1件物品,现在要考虑第i件物品;当前方案已包含的物品的重量之和为tw;至此,若其余物品都选择是可能的话,本方案能达到的总价值的期望值为tv。算法引入tv是当一旦当前方案的总价值的期望值也小于前面方案的总价值maxv时,继续考察当前方案变成无意义的工作,应终止当前方案,立即去考察下一个方案。因为当方案的总价值不比maxv大时,该方案不会被再考察,这同时保证函数后找到的方案一定会比前面的方案更好。 $ H( S* X) W+ a3 R' l0 f- Y( m, e% K
对于第i件物品的选择考虑有两种可能: ' D2 S* j+ k5 o
' |2 W7 P! J1 t (1) 考虑物品i被选择,这种可能性仅当包含它不会超过方案总重量限制时才是可行的。选中后,继续递归去考虑其余物品的选择。 3 _% I6 }9 l2 W9 H2 m/ f. O' K# p+ D( z1 V, P
(2) 考虑物品i不被选择,这种可能性仅当不包含物品i也有可能会找到价值更大的方案的情况。 6 k7 a" w% C& k: X' v+ Z: o4 i% B/ F v W; ?* }
按以上思想写出递归算法如下: $ g$ g7 _" \1 g+ Y z! C5 T" w+ \% H g1 {& u J; D* {2 `- ]
try(物品i,当前选择已达到的重量和,本方案可能达到的总价值tv) ( F R9 c8 O$ ^2 Y - ~* r% T* o! q { /*考虑物品i包含在当前方案中的可能性*/ * k& l; H8 b% A3 K8 @+ B: [ , d. d+ l- P% Q! G if(包含物品i是可以接受的) / o b% C) @. b9 p- F " B% r9 B; o! k& ^ { 将物品i包含在当前方案中; ; \0 {) U; w& `% _; n8 Z
& `+ i& y* }. } if (i 4 K+ [- h/ ^4 u9 c o: V. a
* t; E! b7 w0 w1 }2 P) [ try(i+1,tw+物品i的重量,tv); / Z! b; A1 F' j, ], Q. v2 t
" p! t0 s6 O6 L/ @+ G: h
else 9 v/ W% F" T- L ?$ J
" _7 f7 b! R$ F- E
/*又一个完整方案,因为它比前面的方案好,以它作为最佳方案*/ ) d7 g6 Q+ L ~. x7 y
- L7 y9 X. O$ O; D V7 {. K9 t$ u
以当前方案作为临时最佳方案保存; 8 _; Y5 M8 Z* L# V4 A5 C! _& f" Q- h, V& m/ e# |
恢复物品i不包含状态; ! y5 S. X% N9 M. U0 E- U1 ]$ C) C, d2 p0 @( X. t9 u
} ! A' d9 m3 W; x4 l& {
7 Q5 ~3 \3 Q' i4 E /*考虑物品i不包含在当前方案中的可能性*/ % X+ ]/ h: |$ s9 x+ ?4 b Z/ z- v 6 \8 g0 ~$ ^- o( k' t/ s if (不包含物品i仅是可男考虑的) , E9 Y6 A$ a% p* p: y( P1 [' I. n6 x! {
if (i 7 w; J2 S. y1 k- N
& P6 A3 h, E4 a* ?5 S try(i+1,tw,tv-物品i的价值); 6 A }9 }& L* S & V/ B+ C" H& b& X) P5 [' r else + D" R( u0 x: t( m9 X - Z. J$ Z# \& v9 d3 A6 w* ?+ Y /*又一个完整方案,因它比前面的方案好,以它作为最佳方案*/ 2 q% X* r" U i: y: `7 ~# q7 X! G0 `* K. W- s* H, V
以当前方案作为临时最佳方案保存; : N2 L1 v# G* o- b2 c2 @
* Y3 A; a, M) g4 J% k
} - Z) R8 Q0 i! c7 b , |1 _. G/ V1 j 为了理解上述算法,特举以下实例。设有4件物品,它们的重量和价值见表: & O, b9 @# J3 i! d& \6 `: k
+ ~3 m6 w" s$ R 物品 0 1 2 3 & F2 o2 H/ c" [2 Y
8 T* Q) a* C% y' O. v9 | 重量 5 3 2 1 " `; n1 f; c+ d& i7 j
( T0 h* h4 l' `
价值 4 4 3 1 - \, O6 A9 L' O ( q! B# O1 x$ ]' O 并设限制重量为7。则按以上算法,下图表示找解过程。由图知,一旦找到一个解,算法就进一步找更好的佳。如能判定某个查找分支不会找到更好的解,算法不会在该分支继续查找,而是立即终止该分支,并去考察下一个分支。 - `1 h5 q6 g& ^% @/ \; O% r H u+ ?" V2 B$ U" n4 h) K z" X
按上述算法编写函数和程序如下: : F4 A* e* \) v6 n: Q) t c k
" [5 w0 C% O7 c, F 【程序】 % s7 k7 N# i5 M" U; I, e1 \& W; |) t- }+ U
# include ' t# X( G' E, c2 K* {1 y* W
1 H+ r) Z7 ]1 `) W8 B # define N 100 # C" H0 J1 O U t$ d ( W# U& a" [7 i+ ?2 s; V double limitW,totV,maxV; e" M" y q6 o- A* r$ p$ D: q8 n0 }1 S C
int option[N],cop[N]; . q" d3 T. U0 H3 p5 l3 c) z* f5 b
7 Q( _% @$ A7 M0 G- ?5 k6 {
struct { double weight; ! K+ Y; U; F* t6 b1 H- N1 Z! Q% G
5 r/ Y, ?/ C7 t double value; 3 @* O* [% i6 j- i) j$ A - k1 U& N/ ~# T$ h- w+ \7 L }a[N]; & @8 D% p- I) l/ X$ G, ?8 I) h: R: ?4 V6 o+ }0 J% k3 ~
int n; x& p7 l6 F7 ?, Y 8 G' M+ Q! N* s% z: \( w void find(int i,double tw,double tv) 8 ?! B. ~ A( _5 g6 }, g Z , S; N& h" A- v) p7 K7 } { int k; 9 Z5 }; U; z: ]$ r5 r" h; \. u7 n# {! K4 E& U: r( c9 s2 L
/*考虑物品i包含在当前方案中的可能性*/ $ b+ b( g: R: _% R' `+ l
8 }: B" ?. V8 l$ [4 }: l( ?
if (tw+a.weight<=limitW) # [# H- L0 k3 R0 W5 U: y, `' i8 O: t' O
{ cop=1; 7 z8 D3 m- O6 d4 F- w& V" c+ W3 k8 ~% b) ^" L4 ~0 n% p# O0 C
if (i + z/ O$ X \0 t' q6 s& J- L ( C- _' ?3 d, P- z2 H% v$ _+ D else : s; R7 s; U7 L
, B% m0 X$ _, u { for (k=0;k N" Z3 T, f3 B j$ s; @( \6 w
. ]# Q6 ~+ j4 L( u5 y9 M' z
option[k]=cop[k]; " f, A) v& [6 X0 t# u Z9 @ ) @4 _# N$ ^- U4 W9 _, H1 { maxv=tv; : f0 [% e$ A1 u! }6 j: ?! C 6 u+ Y$ C: }. f8 R% l } ( g. {# g; |+ n. i
6 C ` Z7 j3 U: }0 E0 @9 K
cop=0; 3 t( O- T- ?5 Y6 p! s
! c2 ~1 _9 ]6 i9 `) c } ; h/ ~ I4 w! c; } ( g- w& M0 F: r j2 H /*考虑物品i不包含在当前方案中的可能性*/ 7 g0 Z! V% {4 r& Z6 [2 Q2 r i9 J2 E& Q
if (tv-a.value>maxV) 5 p% n" q v4 T3 {; {, ~4 E+ e1 b/ \1 b9 G# [
if (i 5 m+ G; c) r8 S k9 d; h! t( o% K( R/ S0 M- \) e0 T
else / J7 o- {, ?4 f
, M$ h* N4 c. c$ L" R% |/ j. p { for (k=0;k 0 A3 R3 j5 a! R9 y3 I3 b/ i 4 Z8 E& d) M" g/ e8 Y option[k]=cop[k]; 9 A& T2 I3 H% c5 _