素数环问题(递归枚举)

1. 递归有点像盗梦空间。。。

每做一次递归就深入一层,如果某一层递归里满足某条件要return,就return到上一层递归语句,执行它的下一行,以此类推。

如素数环问题(部分):

void DFS(int num)

{

        ... ... ; 

        for(int j=2; j<num; j++)

    {

          if(!isPrime(ans[j-2]+ans[j-1]))  {  return;  }   //如果不是素数,返回继续枚举

     }

        for(int i=1; i <= n; i++)

{

            if(circle[i]==false)

{

              circle[i]=true;

              ans[num]=i;

              DFS(num+1);      //递归枚举

             circle[i]=false;

}   }   }

比如,num==4时,在嵌套里执行DFS(num+1),即DFS(5);

若此时不满足素数条件要return,return到circle[i]=false;

若此时满足for循环退出条件,说明DFS(4)的递归结束,返回到DFS(4)调用处(注意此时不是返回到main函数,而是一层一层递归,一层一层深入,一层一层浅出);

此时DFS(4)为DFS(3+1),num==3;

以此类推。


2. 解决素数环用回溯法枚举每一个值,分为两步:

第一,当第一个数位为1确定时,尝试放入第二个数,判断这两个相邻数的和是否为素数,若满足条件,再放入第三个数,以此类推(放置过的数做个标记,以防再次放置);

第二,若当前位置无论放任何之前未被放置过的数都无法满足条件,那么回溯改变上一个数;


以上两步,恰好可以通过递归“进出”解决。


3. 代码如下:

#include <iostream>
using namespace std;


int ans[22];  //ans数组脚标代表环中对应的位置
bool circle[22];  //circle数组脚标代表当前数字,数组值代表当前数字是否放入环中
int n;  //n代表输入的数,环中待排列数字总数
      //n取值范围为1<n<17


bool isPrime(int x)   //判断x是否是素数,是素数返回TRUE,反之返回FALSE
{
if (x<=1)
{
return false;  //小于等于1的数,必然不是素数
}
int bound = (int)sqrt(x) + 1;
for (int index = 2; index < bound; index++)
{
if (x%index == 0)
{
return false;
}
}
return true;
}


void DFS(int num)  //递归枚举,num表示当前进行到环中的第几个位置,从0开始
{
int i, j;
if (num>0 && ans[0]!=1)  //环的第一个数必须是1
{
return;
}


for (j = 2; j <= num; j++)  //当前环中已排列的相邻数字之和是否为素数
{                                //j表示位置
if (!isPrime(ans[j - 2] + ans[j - 1]))
{
return;  //不是素数则返回继续枚举
}
}
for (i = 1; i <= n; i++)   //i表示1~n,需填入环中的数字
{
if (!circle[i])
{
circle[i] = true;
ans[num] = i;
DFS(num + 1);  //列举下一个数
circle[i] = false;
}
}  //当前DFS函数递归完return到上一层的DFS函数,如,由DFS(4)回到DFS(3)
  //回到DFS(3)后,下一行执行circle[i]=false;
//此时的i是发现当前位置无论如何无法满足条件后,回溯改变的上一个数
if (num==n)
{
if (isPrime(ans[num-1] + ans[0]))  //如果是素数,进行下一步
{
for (j = 0; j < n; j++)   //index表示位置
{
cout << ans[j] << " ";
}
cout << endl;
return;
}
}


}


int main()
{
int i, j;
int cas = 0;
while (cin>>n)   //n范围从1到17
{
for (int t = 0; t < 22; t++)
{
circle[t] = false;
}


memset(ans, 0, 22 * sizeof(int)); //每轮将circle数字重新初始化
cas++;
cout << "case " << cas << ":" << '\n';
if (n%2!=0)
{
cout << endl;  //若n为奇数,无法形成素数环,因为奇数+奇数=偶数,会多一个奇数
}
else
{
DFS(0);  //位置从0开始
}
}
return 0;
}



版权声明:本文为qq_29442755原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。