goj 素数环(dfs)
Problem Description:
如图所示,环由n个圆组成。 将自然数1,2,...,n分别放入每个圆中,并且两个相邻圆中的数字总和应为素数。
注意:第一个圆圈的数量应该始终为1。
Input:
n (0 < n < 20).
Output:
输出格式如下所示。 每行代表从1开始顺时针和逆时针旋转的一系列圆圈数字。 数字的顺序必须符合上述要求。 按照字典顺序打印解决方案。
你要编写一个完成上述过程的程序。
即使没有答案,也会在每个案例后打印一个空行。
Sample Input:
6 8
Sample Output:
Case 1: 1 4 3 2 5 6 1 6 5 2 3 4 Case 2: 1 2 3 8 5 6 7 4 1 2 5 8 3 4 7 6 1 4 7 6 5 8 3 2 1 6 7 4 3 8 5 2
解题思路:dfs。先打表记录40以内是素数的数组isp,因为相邻数之和有可能最大为39,所以只需枚举到39。关于字典序输出,这里每枚举到当前这个数,从2开始到n枚举哪些数是满足条件,有的话就将当前这个数记录在path数组中,并且标记这个数已经被访问,再递归下去寻找下一个数,如果递归不满足条件的话,将当前这个数置为0。每当cur==n时就输出当前素数环。其中有一个剪枝的操作,如果给定的整数n为奇数,那么肯定不存在素数环,(因为肯定存在两个奇数相邻,而奇数与奇数的和为偶数,所以一定不是素数环)这个节省了不少递归时间,不然老是TLE-_-||。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,isp[40]={0},vis[20],path[20];//vis数组是保存标记,path是记录 4 int is_prime(int t)//判断素数 5 { 6 for(int i=2;i*i<=t;++i) 7 if(t%i==0)return 0; 8 return 1; 9 } 10 void dfs(int cur) 11 { 12 if((cur==n) && isp[path[1]+path[n]]){//递归边界 如果cur==n && isp(1+path[n])是素数,则终止条件 13 for(int i=1;i<n;i++) 14 cout<<path[i]<<' '; 15 cout<<path[n]<<endl; 16 return ; 17 } 18 else{ 19 for(int i=2;i<=n;++i){//尝试放置每个数i 20 if(!vis[i] && isp[i+path[cur]]){ //如果i没用过 21 path[cur+1]=i;//且i加上与之相邻的上一个数之和是素数,则把它赋给path[cur+1]; 22 vis[i]=1; //设置使用标志 23 dfs(cur+1); //深搜 24 vis[i]=0; //清除标志 25 } 26 } 27 } 28 } 29 int main() 30 { 31 for(int i=2;i<40;++i) //生成素数表,枚举到最大的2倍即可 32 isp[i]=is_prime(i); 33 int k=1; //Case情况数 34 while(cin>>n){ 35 cout<<"Case "<<k++<<':'<<endl; 36 memset(vis,0,sizeof(vis)); 37 vis[1]=1,path[1]=1;//将1作为path开头,且标记已访问 38 if(n>0 && (n%2==0))dfs(1);//剪枝,如果是偶数的话,必有素数环,从1开始深搜 39 cout<<endl; 40 } 41 return 0; 42 }