zoukankan      html  css  js  c++  java
  • 小学数学题,你会吗?

      一日,某小学生问作业:“将16分解为若干素数的和,求这些素数积的最大值”。不禁被吓了一跳。怎么小学生的数学题变得这么难了?
      细细询问,小学生没学不等式,没学数学归纳法……。那么只能用最笨的办法——穷举,一个个地试的办法来解决。
      穷举之道,在于一一举来,不多不少;而不多不少,则在于有条有理,从容不乱。
      小于16的素数依次为:2,3,5,7,11,13。显然,最大积是16和{2,3,5,7,11,13}的函数,将这个最大积记为
        F(16,{2,3,5,7,11,13})
      该最大积中可能有素因子2也可能没有,因此

        F(16,{2,3,5,7,11,13}) =
         MAX ( 
                   2 * F(14 ,{2,3,5,7,11,13}) ,
                   F(16 ,{3,5,7,11,13} ) ,
               )
      同理,
        F(14,{2,3,5,7,11,13}) =
         MAX ( 
                   2 * F(12 ,{2,3,5,7,11,13}) ,
                   F(14 ,{3,5,7,11,13} ) ,
               )

        F(16,{3,5,7,11,13}) =
         MAX ( 
                   3 * F(3 ,{2,3,5,7,11,13}) ,
                   F(16 ,{5,7,11,13} ) ,
               )
        ……
      由此不难看出这构成了一个递归过程,终止的条件为F(n,{})中的素数集合为空或n<=0。
      下面用C语言描述这个过程。
      用程序解决问题显然不应该只解决分解16这样单独的问题,而应该至少能解决一类问题。为此将问题描述为:
      将正整数n分解为若干素数的和,求这些素数积的最大值。

    #include <stdio.h>
    
    void input( unsigned * );
    unsigned maxmul( unsigned , 素数集合类型 );
    unsigned maxmul_(  unsigned , 素数集合类型 );
    unsigned max( unsigned , unsigned );
    
    int main( void )
    {
       
       unsigned n ;
       素数集合类型 素数集合; //这里需要一个素数集合;
    
       input( &n );                             //输入n   
       printf("%u
    ", maxmul( n , 素数集合 ) ); //输出最大积
    
       return 0;
    }
    
    unsigned max( unsigned u1 , unsigned u2 )
    {
       return u1 > u2 ? u1 : u2 ;
    }
    
    unsigned maxmul_( unsigned n , 素数集合类型 素数集合 )
    {
       if ( 素数集合为空 ||  n < 素数集合中的最小元素 )
       {
          return 0;
       }
       if ( n == 素数集合中的某个元素 )
       {
          return n;
       }
       return max (
                     素数集合中的某元素 * maxmul_(  n - 素数集合中的某元素 , 素数集合 ) ,
                     maxmul_( n , 素数集合删掉一个元素 ) 
                  );
    }
    
    unsigned maxmul( unsigned n , 素数集合类型 素数集合 )
    {
       if ( n < 4u ) // 小于4的情况无解
          return 0;
    
       return maxmul_( n , 素数集合 );
    }
    
    void input( unsigned * p )
    {
      puts( "输入n:" );
      scanf( "%u" , p );   
    }

       至此,还需要给出不大于n的素数集合。由于不清楚不大于n有多少素数,所以用数组表示这个集合显然不现实,即使用C99的VLA也不够好。
      那么只好用链表。问题就成了给出不大于正整数n的素数链表。链表用下面的数据结构描述:

    typedef 
    struct prime_list
       {
          unsigned prime;
          struct prime_list * next;
       } 
    * P_LIST;

       在main()中定义这个链表:

    P_LIST pr_list = NULL ;

       根据n求得这个链表

    pr_list = build( n ); 

       令我没想到的是这个函数不那么容易写,稍不留神就错。你们体会下!

    typedef 
    struct prime_list
       {
          unsigned prime;
          struct prime_list * next;
       } 
    * P_LIST;
    
    typedef
    enum 
       { 
          NO ,
          YES, 
       } 
    YESNO ;
    
    P_LIST build( unsigned  );
    void build_( P_LIST * , P_LIST * , unsigned , unsigned );
    YESNO be_prime( unsigned , P_LIST );
    void add ( P_LIST * * , unsigned ) ;
    void my_malloc( P_LIST * );
    
    void my_malloc( P_LIST * pp )
    {
       if ( ( * pp = malloc( sizeof (* * pp) )) == NULL )
          exit(1);
    }
    
    void add ( P_LIST * * ppp_e, unsigned const num ) 
    {
       my_malloc( * ppp_e ); 
       ( * * ppp_e ) -> prime = num ;
       ( * * ppp_e ) -> next  = NULL;
       * ppp_e = & ( * * ppp_e ) -> next ;
    }
    
    
    YESNO be_prime( unsigned n , P_LIST p )
    {
       if ( n == 2u || p == NULL )
       {
          return YES ;
       }
       if ( n % p -> prime == 0u )
       {
          return NO ;
       }
       return be_prime( n , p -> next );
    }
    
    void build_( P_LIST * pp_b , P_LIST * pp_e , 
                 unsigned num , unsigned n )
    {
       if( num > n )
       {
          return ;
       }
       if ( be_prime( num , *pp_b ) == YES  )
       {
          add ( &pp_e , num ) ; //将num加入链表
       }
       build_( pp_b ,  pp_e , num + 1u , n ) ; 
    }
    
    P_LIST build( unsigned n )//建立不大于n的有序素数链表 
    {
       P_LIST head = NULL ;
       build_( &head , &head , 2u , n );  //从2开始
       return head;
    }

       最后是完整的代码。

    #include <stdio.h>
    
    typedef 
    struct prime_list
       {
          unsigned prime;
          struct prime_list * next;
       } 
    * P_LIST;
    
    typedef
    enum 
       { 
          NO ,
          YES, 
       } 
    YESNO ;
    
    void input( unsigned * );
    P_LIST build( unsigned  );
    void build_( P_LIST * , P_LIST * , unsigned , unsigned );
    YESNO be_prime( unsigned , P_LIST );
    void add ( P_LIST * * , unsigned ) ;
    void my_malloc( P_LIST * );
    unsigned maxmul(  unsigned , P_LIST );
    unsigned maxmul_(  unsigned , P_LIST );
    unsigned max( unsigned , unsigned );
    void my_free( P_LIST );
    
    int main( void )
    {
       
       unsigned n ;
       P_LIST pr_list = NULL ; 
       
       input( &n );                            //输入n
       pr_list = build( n );                   //准备素数表
       printf("%u
    ", maxmul( n , pr_list ) ); //输出
       my_free( pr_list );
    
       return 0;
    }
    
    void my_free( P_LIST p )
    {
       if ( p != NULL )
       {
          free( p -> next );
          free( p );
       }
    }
    
    unsigned max( unsigned u1 , unsigned u2 )
    {
       return u1 > u2 ? u1 : u2 ;
    }
    
    unsigned maxmul_( unsigned n , P_LIST p )
    {
       if ( p == NULL ||  n < p->prime )
       {
          return 0;
       }
       if ( n == p->prime )
       {
          return n;
       }
       return max (
                     p -> prime * maxmul_(  n - p->prime , p ) ,
                     maxmul_( n , p -> next ) 
                  );
    }
    
    unsigned maxmul( unsigned n , P_LIST p )
    {
       if ( n < 4u )
          return 0;
    
       return maxmul_( n , p );
    }
    
    void my_malloc( P_LIST * pp )
    {
       if ( ( * pp = malloc( sizeof (* * pp) )) == NULL )
          exit(1);
    }
    
    void add ( P_LIST * * ppp_e, unsigned const num ) 
    {
       my_malloc( * ppp_e ); 
       ( * * ppp_e ) -> prime = num ;
       ( * * ppp_e ) -> next  = NULL;
       * ppp_e = & ( * * ppp_e ) -> next ;
    }
    
    
    YESNO be_prime( unsigned n , P_LIST p )
    {
       if ( n == 2u || p == NULL )
       {
          return YES ;
       }
       if ( n % p -> prime == 0u )
       {
          return NO ;
       }
       return be_prime( n , p -> next );
    }
    
    void build_( P_LIST * pp_b , P_LIST * pp_e , 
                 unsigned num , unsigned n )
    {
       if( num > n )
       {
          return ;
       }
       if ( be_prime( num , *pp_b ) == YES  )
       {
          add ( &pp_e , num ) ; //将num加入链表
       }
       build_( pp_b ,  pp_e , num + 1u , n ) ; 
    }
    
    P_LIST build( unsigned n )//建立不大于n的有序素数链表 
    {
       P_LIST head = NULL ;
       build_( &head , &head , 2u , n );  //从2开始
       return head;
    }
    
    void input( unsigned * p )
    {
      puts( "输入n:" );
      scanf( "%u" , p );   
    }
    View Code

      运行结果:

    输入n:
    16
    324

    题外话:

     

      从数学的角度看,这个题目并不难。只要运用初中数学知识,就不难分析出,对于大于3的正整数n的最大素数积,当n为

        6k型正整数时,分为2k个3积最大;
        6k+1型正整数时,分为2k-1个3、2个2积最大;
        6k+2型正整数时,分为2k个3、1个2积最大;
        6k+3型正整数时,分为2k+1个3积最大;
        6k+4型正整数时,分为2k个3、2个2积最大;
        6k+5型正整数时,分为2k+1个3、1个2积最大。
      结论用数学归纳法很容易证明。参见http://bbs.chinaunix.net/thread-4088334-2-1.html

    【补记】

      建立素数链表部分写得很复杂。今天突然想到原因之一是建立的是有序表,但“有序”在这里其实是不必要的。如果建立的是一个逆序链表,代码要简单很多。

          2013,7,23

    //【题目:将16分解为若干素数的和,求这些素数积的最大值】
    //用逆序素数表
     
    #include <stdio.h>
    
    typedef 
    struct prime_list
       {
          int prime;
          struct prime_list * next;
       } 
    * P_LIST;
    
    typedef
    enum 
       { 
          NO ,
          YES, 
       } 
    YESNO ;
    
    
    void  input( int * );
    void  build( P_LIST * , int );
    YESNO be_prime( int , P_LIST );
    void  add( P_LIST * , int );
    void my_malloc( P_LIST * );
    void my_free( P_LIST );
    int  maxmul( int , P_LIST );
    int  maxmul_( int , P_LIST );
    int  max( int , int );
    
    #if 0 //测试 
    void out( P_LIST );
    void out( P_LIST p )
    {
       while ( p != NULL )
       {
          printf("%d ",p->prime);
          p = p -> next ;
       }
       putchar('
    ');
    }
    #endif  
    
    int main( void )
    {
       
       int n ;
       P_LIST pr_list = NULL ; 
       
       input( &n );                            //输入n
       build( & pr_list , n );                 //建立不大于n的素数表
       //out( pr_list );                       //测试 
       printf("%d
    ", maxmul( n , pr_list ) ); //输出
       my_free( pr_list );
       
       return 0;
    }
    
    int max( int n1 , int n2 )
    {
       return n1 > n2 ? n1 : n2 ;
    }
    
    int  maxmul( int n , P_LIST p )
    {
       if ( n < 4 )
          return 0;
       return  maxmul_( n , p ) ;
    }
    
    int  maxmul_( int n , P_LIST p )
    {
       if ( n < 0 )
          return 0;
    
       if ( n == 0 )
          return 1;
    
       if ( p == NULL )
          return 0;
    
       if ( n < p->prime )
          return maxmul_( n , p->next ) ;
          
       return max(
                   p->prime * maxmul_( n - p->prime , p ), 
                   maxmul_( n , p->next ) 
                 );   
    }
    
    void my_free( P_LIST p )
    {
       P_LIST tmp ;
       while ( ( tmp = p ) != NULL )
       {
          p = p->next; 
          free( tmp );
       }
    }
    
    void my_malloc( P_LIST * p_p )
    {
       if ( ( * p_p = malloc( sizeof (* * p_p) )) == NULL )
          exit(1);
    }
    
    void  add( P_LIST * p_p , int n )
    {
       P_LIST tmp ;
       my_malloc( &tmp );
       tmp->prime= n ;
       tmp->next = * p_p ;
       * p_p = tmp ;
    }
    
    YESNO be_prime( int n , P_LIST p)
    {
       while ( p != NULL )
       {
          if ( n % p->prime == 0 )
          {
             return NO;
          }
          p = p->next ;
       }
       return YES;
    }
    
    void build( P_LIST * p_p , int n )
    {
       int i ;
       for ( i = 2 ; i <= n ; i++ )
       {
          if ( be_prime( i , *p_p ) == YES ) //如果i是素数 
          {
             add( p_p , i );                 //将i加入链表 
          }
       }
    }
    
    void input( int * p )
    {
      puts( "输入n:" );
      scanf( "%d" , p );   
    }
    View Code
  • 相关阅读:
    node.js学习
    CSS3——3D效果
    解决HTML5布局,兼容IE问题
    Java 反射调用的一种优化
    jQuery中常用正则表达式
    8种NoSql数据库系统对比
    Java的ClassLoader机制
    Maven / Nexus 的用法和经验
    Spring中BeanFactory和ApplicationContext的区别
    Spring Bean的作用域
  • 原文地址:https://www.cnblogs.com/pmer/p/3206691.html
Copyright © 2011-2022 走看看