关于相邻两数之和为素数的解答

    技术2022-05-11  174

     题目:巧排数字。将1、2、...、20这20个数排成一排,使得相邻的两个数之 和为一个素数,且首尾两数字之和也为一个素数。编程打印出所有的排法。

        先说下这道题的思路,肯定是应该用回溯的方法。穷举则运算的量太大不适合,而递归空间复杂度也太大。

    先放入第一个数 1,然后依次放入2、3、4···,每次放入时判断与前一个数之和是否为素数,若是则继续放入,若不是则回退一步,选后一个数。这样直到放完。

     伪算法:

    0. 初始化 标记所有数在每个位置都能放置。

    1. 从剩下的数选最小的一个且能在此位置放置的,放入数组,若没有数满足到3,若已放完到4.

    2. 判断放入的数与前一个数之和是否为素数(第一个不判断,最后一个要与它前一个和第一个数判断)    是素数回到1,不是素数,标记此数不能在这个位置放置到1.

    3. 回退一步,即将前面一个数取出,并且标记此位置之后的位置,所有数都能放置。到1.

    4. 打印出结果。到5.

    5. 判断是否退空,若没退空到3.。退空结束。

    这里我将放数,退数模仿成进栈、出栈。然后输出结果到文件时声明了

    一个友元函数,方便一些。

    下面是源代码 ( 编译器 VC++ 8.0 )

    // Stack.h #ifndef STACK_H #define  STACK_H class  Stack{     public :        Stack();         bool  Push();             bool  Pop();                 void  Display();         bool  IsFull();         int  GetTop();         long  TheCount();        friend  void  FileDisplay( const  Stack & );         // 输出到文件          private :         bool  IsPrime( int );     private :         long  count;                 // 解数          int  top;         int  value[ 21 ];             // 结果          bool  state[ 21 ][ 21 ];      // 状态    [位置][数] }; #endif    // STACK_H /// :~

     

    // Stack.cpp #include  " Stack.h " #include  < iostream > #include  < fstream > #include  < cstring >      // memset() #include  < math.h > using   namespace  std;Stack::Stack(){    memset(value, 0 , 21 * sizeof ( int ));    memset(state, true , 21 * 21 * sizeof ( bool ));    top = 0 ;    count = 0 ;} void  Stack::Display(){     for ( int  i = 1 ;i <= 20 ;i ++ )        cout << value[i] << " " ;    cout << endl;} bool  Stack::IsFull(){     return  (top == 20 );} bool  Stack::Push(){       if (top == 20 )     // 已填完           return   false ;        top ++ ;     for ( int  i = 1 ;i <= 20 ;i ++ )    {         // 测试i是否满足          if (state[top][i])        {             bool  flag = true ;             for ( int  j = 1 ;j < top;j ++ )            {                // 判断前面是否用过此数                  if (value[j] == i)                {                    flag = false ;                     break ;                }            }             if (flag)            {     if (top == 20 )                    {           if ( ! IsPrime(i + value[ 1 ]))     // 最后一个数要判断与第一个数之和是否为素数           {                 state[ 20 ][i] = false ;                  continue ;           }    }                                                      if (IsPrime(i + value[top - 1 ]))     // 判断与其前一个数之和是否为素数                     {                                                state[top][i] = false ;                            value[top] = i;            if (top == 20 )                count ++ ;            // 填完                              return   true ;                     }                      else                      {                           state[top][i] = false ;                     }            }        }    }    top -- ;     return   false ;     // 没有满足条件的数 返回false } bool  Stack::Pop(){        if (top == 0 )           // 栈空返回false             return   false ;       state[top][value[top]] = false ;       memset(state[top + 1 ], true , 21 * sizeof ( int ));     // 其后位置所有数可再用                value[top] = 0 ;       top -- ;       return   true ;} bool  Stack::IsPrime( int  e){     if (e % 2 == 0 )         return   false ;     for ( int  i = 3 ;i <= sqrt(( float )e);i += 2 )    {         if (e % i == 0 )             return   false ;    }     return   true ;} int  Stack::GetTop(){     return  top;} long  Stack::TheCount(){     return  count;} void  FileDisplay( const  Stack &  a){       ofstream  out ( " Answer.txt " ,std::ios_base::app);        out << " No. " << a.count << endl;        for ( int  i = 1 ;i <= 20 ;i ++ )             out << a.value[i] << " " ;        out << endl;}

     

    memset(char* ,value,long)这个函数初始化比较快一点,它把起始于某一特定地址的内存(该内存作为第一个参数)从起始地址直至气候的n(n作为第三个参数)个字节的所有内存都设置成同一个特定的值(该值作为第二个参数)

     

    // main.cpp #include  < iostream > #include  " Stack.h " using   namespace  std; int  main(){    Stack a;     bool  s = true ;     while (s)     // 循环直到栈空,即不能再回退,则所有已测试完     {          if (a.Push())         { /* 继续往内填数 */ }          else          {              s = a.Pop();     // 不能再填则回退一步                  }          if (a.IsFull())         {                         FileDisplay(a);         }     }          return   0 ;}

    这个结果共有1千多万组,这是不考虑相对位置即不把它看成一个圆。

    若有高手有更好算法,还请指点。


    最新回复(0)