数学建模社区-数学中国

标题: 算法入门系列之二 -- 筛选法 [打印本页]

作者: matrix_spaceman    时间: 2004-6-8 16:42
标题: 算法入门系列之二 -- 筛选法
所谓筛选法就是从全集中将不合格的项全部删去,剩下的就是答案。
% L, y3 s. t# W7 T. i$ F8 z, H- M& J) Y  J+ Q% Z/ W1 S
1: 求1-100之间的质数
- n1 O+ @% `. V2 y$ o9 J5 Q9 d+ q# d) Q
    原理:先将2的倍数全部划掉,再将3的倍数全部划掉……直到将10的倍数全部划掉(10^2 = 100 )。剩下的就是质数., }* F( }+ S1 ]+ M7 j

6 E5 S' J- b. u$ ^- G1 w) Pvoid fun1()
' U# X; j# C; h{2 Y( N# q* w! b" o, O
    int i, j;+ |  G- q5 M. g; N) ?9 n
    bool flag[100];
, F) U$ f, ?/ |7 P3 v4 R  R' l/ t( ~" V: i& G, u/ i3 F
    for ( i = 2; i < 100; i++ )9 M  p1 K$ z6 _% q
        flag = true;) x+ X/ B- E- c; v+ j

! T+ |  R: M. l    //下面开始划掉合数
5 n' c& k! q# `5 j& C    for ( i = 2; i < 10; i++ ) //因为合数的最小正因数不大于它的平方根
$ ]+ A0 m" G! Z1 S) O; r    {1 v  {8 ?! j* O( j' Y9 N
        if ( flag == false )2 Y9 C: R4 j" E/ q# p
            continue; //既然i已经是合数,它的倍数应该已经被划掉& ~/ Z2 W  ~2 ]0 N* @; Z# V
        for ( j = 2; j * i < 100; j++ )//划掉i*j; I' w: w/ y/ V# \9 n- S' ?& o
            flag[i*j] = false;
$ i3 M" y! W/ O5 Q3 _  O3 f    }
$ j/ ?& Y' E% b% p( ?0 u/ r" F$ s" p. f# `" v+ H. e* @1 k* @
    //输出结果9 x# l9 T% K' M  a1 J( s
    for ( i = 2; i < 100; i++ ), \7 \$ ]# e+ E2 a+ ?
        if ( flag )
$ Z8 k% G% f6 H) [1 l            cout << i << ' ';: }  W* V4 E8 t1 g5 A
}. x2 g! }3 `% S7 e( F/ r. k; Z
3 ?4 o3 c, u. e. N( t+ F1 [: H
2: 30个人站成一圈,从第一个人开始报数,凡报到5的拉出去毙了,剩下的人接着从1开始报,直到只剩一人放掉。想活命该站哪?. T  J2 r; b! |+ u- n

! D( H: i4 I8 J" E! M    这是个经典问题,解法就是模拟整个过程,有点类似于循环队列。
3 f3 [2 q& l  P; R. v: O; R+ n7 N) t; U' S
    另外,为了程序上处理方便,可以假定所有的人都拉去毙了,然后看一下最后一个被毙掉的是谁。另一处为了处理方便而采取的措施是pos的初始值设为-1.很多时候,对问题的问法稍作变动,或者将初始值、加减变量的位置稍作调整,用程序处理起来会方便很多。# ?1 i/ U1 z7 F/ x

" g% G7 Z$ e' I3 ~% I4 svoid fun2()6 l0 i8 I6 x: ?* [; g  D
{
1 j& P# X$ Z4 ~" x% R6 [/ ^    bool flag[30];//标记第i个人的状态,false已被毙掉,true还没被毙掉, L, f! j# P. L3 f% R. T7 N! ^

& T& p/ S- _' s9 m7 o  a+ x    for ( int i=0; i< 30; i++ )6 q8 M1 D' l3 R  C5 e& x
        flag = true;
  d8 p2 o. @# w4 h' X6 X$ K/ F. R2 C- [* T: O( p
    int n = 30;//还剩几个人
+ _+ b9 e1 B2 H" n$ k    int cnt;
. ~; d4 r5 h  }; Y& w4 g( i% X( ~    int pos = -1;//初始值不一定从0开始,设为-1处理起来较方便+ O* p; \. j7 h, b' b
    while ( n > 0 )$ C1 ?& `9 X! M, _' l. ?" L8 _9 v
    {
' }' n  u$ r1 v) q: J2 P        cnt = 0;
$ }+ D) K2 p/ w& ]$ t1 O        while ( cnt < 5 ). J$ h8 I8 J' F6 |+ ~! e) ]0 V
        {3 w1 M7 u( N3 C  W' \
            if ( ++pos == 30 )
  A! M: A9 q% K                pos = 0;
+ J4 @, \+ m0 g# G- y            if ( flag[pos] )//此人还没被毙掉+ P. @' m* P5 C- h  Z
                cnt ++;
" r) F4 G% Z* x2 Z. S2 C; G* }6 S        }
$ P! @( J9 \" F: @( D" ~, i        //退出时cnt == 5,pos指向报5的人
& F; V$ E) f" I2 ^; ?, x        flag[pos] = false;//毙掉4 b7 w$ Q" m9 Z! y; O
        n --;//剩余人数减1* y2 T* L: \* y' ^
    }5 m6 n+ x% O, {$ U
    //最后一个被毙的就是幸存者% l2 k0 X+ U; Y
    //之所以加1是因为程序中从0开始计数,输出却以1开始计数
! n& V. t/ w+ l; O( U    cout << pos + 1 << ' ';
3 V0 a- z7 x) k6 _}
# E6 j2 x& u9 F# Q' R4 c1 B0 d+ f3 N) x; J9 y7 v( s8 t* D+ V  v
3: 一个数被5除余3,被7除余2,求此数最小为几。
3 m; c7 E3 B! Q
. _4 C, I9 J0 d! T- @% v    筛选法不一定非要标记true/false,有时候也可以使用计数来进行筛选,这个问题就是一例。
& L) p6 |) J$ G) O; {" ?    首先,如果问题有解,则解必小于 5 * 7 = 35,只用在此范围内筛选即可。/ z' B5 m4 h2 \7 K
    在程序处理上,如果一个数满足一个条件,就将计数值加1,最后计数值为3的即为所求。2 u3 J( C5 x1 ]5 {
% E5 i2 J( M9 {, U4 j9 D
void fun3()  F6 q! E& |: g. R& k% o( c
{
# F* E# b+ c; }, k    int a[105];//105 = 5 * 71 j& N0 i4 w0 B1 N7 t
    int i;
4 h$ k0 X- L" d1 Q$ M& _+ J    //计数清0
- F" }- B* n- @: N- ?  h    for ( i = 0; i < 35; i++ )
# ^$ \, u" y' F        a = 0;
7 S9 Z0 l( W, Q6 C' d9 X    //开始筛选
& v+ V; q, V1 M2 L: N% J; J    for ( i = 3; i < 35; i += 5 )! O; k6 ^+ z& Y$ b
        a ++;" t9 y* M2 n8 h% v( C; M: Y( n
    for ( i = 2; i < 35; i += 7 )
: |& i, i: q3 l( Y        a ++;
4 G' e& J! ]5 ]* t, |1 A* b9 a( \7 _/ L: S. T( W
    for ( i = 0; i < 35; i++ )
. B( n6 x: T" o; U        if ( a == 3 )
/ V0 d$ _, X/ m: |* n, l  {0 [        {: a. h/ @8 g) f6 N% N
            cout << i << endl;
4 \% K% `% G+ s# M( k            return;
6 E3 i; z8 N0 _4 u. J" e5 |! `# n        }- e6 Y7 }8 S; }$ s
    //如果执行到这里说明无解( B( r6 |/ t% h: J( ]. K
    cout << "No Answer!" << endl;8 J4 v; M8 k" F6 u& V
}
6 G' C2 j5 x* q  \) O+ }
3 R$ n. e$ _* A, o" L! [4 D: L' j# ^/ A& A2 g. c7 X0 m
总结:/ `! I9 v, {% ^! c- g+ S5 w" A
    筛选法的时间效率较高,但需要一个辅助数组,这就意味着对规模很大的问题不适用。例如第3个问题,条件多1个,需要的空间就翻几倍。解决的办法是:
' I4 ~  L( J* d7 ^( ^
: m7 t" B$ b' `7 Vfor ( int i = 0; i< n; i++ )1 c, _. u$ l1 |8 M0 B: n
{- f, w0 X' v0 s
    if ( i % 5 == 3 && i % 7 == 2 && ... )
% v# s# j4 V  B+ H8 i# J- `        cout << i;
% f1 f& [1 {* E, G3 n5 k( j" r) p}
! X5 ^5 B  E5 \0 p    但相应的,这样做速度就慢了下来。所以还得根据问题的规模来决定使用什么方案。
' W0 w* D1 s; e/ j
0 F; D' g  [- H' c    总体来讲,对于可以条件判断较复杂,且根据前一个(不)符合条件的情况较简单的推出下一个(不)符合条件的情况,(不)符合条件的情况分布较稀疏的问题,用筛选法速度较快。如果第3题中加一个条件被2除余1,用筛选法就不太合算了。
% ^8 s! M- S1 S( C; N* R# g
3 f* M5 [3 I1 s7 ^    最后说明一下,此次给出的程序目的在于说明方法,在效率并不是最高的




欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5