【HDU 1016】Prime Ring Problem(DFS、素数筛)

来源:互联网 发布:linux cp 合并文件夹 编辑:程序博客网 时间:2024/05/16 07:49

Prime Ring Problem


A ring is compose of n circles as shown in diagram. Put natural number 1, 2, …, n into each circle separately, and the sum of numbers in two adjacent circles should be a prime.

Note: the number of first circle should always be 1.
这里写图片描述


Input
n (0 < n < 20).

Output
The output format is shown as sample below. Each row represents a series of circle numbers in the ring beginning from 1 clockwisely and anticlockwisely. The order of numbers must satisfy the above requirements. Print solutions in lexicographical order.

You are to write a program that completes above process.

Print a blank line after each case.


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


题意

已知一个数n,将数字1~n围成一个圆环,要求: 相邻两个数之和为素数。
输出:

  • 数字的方向一致(同顺时针或同逆时针),并保证排列不重复
  • 只有一个数(n==1)时,输出1
  • 输出Case k:(k为数据组数),每一组输出(第一个除外)之前都有一个空行

思路:


  • 要求每两个相邻数之和都为素数,那么只要每次递归调用DFS时判断之前两个数之和是否为素数,另外注意因为是一个,最后还要判断一下最后一个数和第一个数之和是否为素数。
  • 程序开始时求出一个素数数组,只需求出50以内的素数即可,因为数据<20,最大的素数和为17+19=36。此处用的是素数筛法(什么是素数筛法?)求素数。

素数筛法概览

  • 把从1开始的、某一范围内的正整数从小到大顺序排列, 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。依次类推,直到筛子为空时结束
  • 例如: 先把n个自然数按次序排列起来。1不是质数,也不是合数,要划去。第二个数2是质数留下来,而把2后面所有能被2整除的数都划去。2后面第一个没划去的数是3,把3留下,再把3后面所有能被3整除的数都划去。3后面第一个没划去的数是5,把5留下,再把5后面所有能被5整除的数都划去。这样一直做下去,就会把不超过N的全部合数都筛掉,留下的就是不超过N的全部质数

代码示例:

#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#define MAX 51using namespace std;int prime[MAX];//素数数组 bool vis[21]; //访问数组 int n;// 个数 int ans[21];//解答输出数组 void Prime_set()  //筛法求素数 {    //Isprime 0、 IsNotprime 1      for(int i = 2; i<=sqrt(MAX) ;++ i)        if(prime[i] == 0){            for(int j = 2;i*j<=MAX;++j)                prime[i*j] = 1;        }    prime[1] = 0,vis[1]=true;//1虽然不是素数,但在此假设为0,将vis[1]设为true即不会遍历到1 }void DFS(int depth){    if(prime[ans[depth-1]+ans[depth-2]]!=0) return ;  //前两个数之和不是素数退出     if(depth==n+1&&prime[ans[depth-1]+ans[1]]!=0) return ; //当选到最后一个数时,第一个数和最后一个数之和不是素数时退出     if(depth==n+1)  //选到最后一个数,输出     {        for(int i=1;i<=n;i++)         {            if(i==1) cout<<ans[i];             else cout<<" "<<ans[i];         }            cout<<endl;    }    for(int i=2;i<=n;i++)   //把1~n按照一定顺序(DFS求得)填入数组ans     {        if(!vis[i])         {            vis[i]=true;            ans[depth]=i;            DFS(depth+1);            vis[i]=false;        }    }}int main(){    int t=1;     Prime_set();    while(cin>>n)    {        cout<<"Case "<<t++<<":"<<endl;        memset(vis,false,sizeof(vis));        ans[1] = 1;//1永远是首元素         if(n==1) cout<<"1"<<endl;        else             DFS(2);//1永远是首元素,从2开始DFS ;也防止之后depth-2<0         cout<<endl;    }       return 0;}
注:
  • Q:1不是素数,为何要prime[1] = 0?
    • A:1虽然不是素数,但在此假设为0。因为之后判断(判断两个相邻素数和的时候)会出现一些问题:
      if(prime[ans[depth-1]+ans[depth-2]]!=0)
      在主函数初始进入DFS(2)时,这一句之中的depth-2等于0,ans[0]不在讨论范围之中,故仍然为0,而ans[1]为1,ans[depth-1]+ans[depth-2]=1,prime[1]如果不等于0的话,将会直接return。
原创粉丝点击