hdu 1016 Prime Ring Problem

来源:互联网 发布:psd下载软件 编辑:程序博客网 时间:2024/03/28 20:36
Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 14516    Accepted Submission(s): 6623


Problem Description
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
68
 

Sample Output
Case 1:1 4 3 2 5 61 6 5 2 3 4Case 2:1 2 3 8 5 6 7 41 2 5 8 3 4 7 61 4 7 6 5 8 3 21 6 7 4 3 8 5 2
 



题意: 有N个数字, 依次为1~N, 然后要令这些数字练成一个圆圈, 并且要相邻两个数字的和是一个素数,将所有情况依次输出,并且都是从 1 开始输出。


思路:  两个偶数 或者 两个奇数相加是个偶数, 偶数一定不是素数, 所以N 为奇数时,是没有满足的数字串,输出空行。
然后就用 递归回溯 依次暴力一遍。 每确定一个数字就到下个数字, 满了n个就输出, 回溯的上一个,看还有无其他符合的数字。
深搜,  每次只要考虑    当前要确定的数字    和     上一个确定的数字    的和是否为素数, 和数字是否重复用过。
模拟 N = 6;(希望耐心看)
  首先 : 1 是确定的。 
数组 :  num[]   =    1 __ __ __ __ __;   visitied[]  =  1 0 0 0 0 0; 
然后开始递归了(深搜)  每次都从 2 (i == 2) 数字开始找。 判断   i  与 上个确定的数 是否满足条件   if( (i + num[x - 1])  是素数 && i 未被访问(使用,出现)
结果 是:   num[] = 1 2 __ __ __ __;  visited[] = 1 1 0 0 0 0;
然后递归到下一个要确定的数字; 同理
结果是: num[] = 1  2  3 __ __ __; visited[] = 1 1 1 0 0 0 ;
接着依次是: num[] = 1 2 3 4 __ __; visited[] = 1 1 1 1 0 0;
接着找不到了 因为 4 与 5 和 6 都是合数。4位置就退出递归,将4位置的数字回溯,visited[i][j] = 0;  比较为未访问: num[] = 1 2 3 __ __ __; visited[] = 1 1 1 0 0 0;
退到3后, 3 与 5 和 6 的和都是合数,同理  回溯: num[] = 1 2 __ __ __ __; visited[] = 1 1 0 0 0  0;
退到2后,2 与 5 的和是素数  num[] = 1 2 5 __ __ __; visited[] = 1 1 0 0 1 0;
接着递推: num[] = 1 2 5 6 __ __; visited[] = 1 1 0 0 1 1;
6与 3 和 4 的和都是合数 回溯: num[] = 1 2 5 __ __ __; visited[] = 1 1 0 0 1 0;
5 已经循环到 6 了 退出递归。 回溯:1 2 __ __ __ __;
2 和 6 的和是合数:1 4 __ __ __ __(继续循环) ->  1 4 3 __ __ __  (递归)-> 1 4 3 2 __ __  (递归) -> 1 4 3 2 5 __  (递归) -> 1 4 3 2 5 6 (递归);
满了, 判断头尾的和是否为素数   1 + 6 == 素数   则输出: 1 4 3 2 5 6
然后退出递归,继续回溯: 1 4 3 2 5 __ -> 1 4 3 2 __ __ (回溯)-> 1 4 3 __ __  __(回溯) -> 1 4 __ __ __ __(回溯) -> 1 6 __ __ __ __(继续循环) 
然后继续递归  1 6 5 __ __ __ (递归) -> 1 6 5 2 __ __(递归) -> 1 6 5 2 3 __ (递归) -> 1 6 5 2 3 4(递归);
满了, 同理  1 + 4 == 素数  输出: 1 6 5 2 3 4;
继续 回溯:1 6 5 2 3 __ -> 1 6 5 2 __ __ -> 1 6 5 __ __ __ -> 1 6 __ __ __ __ -> 1 __ __ __ __ __ (循环完退出最外面的dfs(), 程序退出);

递归 可以理解成 深搜, 往更里深搜,  回溯可以理解成 将递归的所访问的 恢复到没访问。

#include <stdio.h>#include <string.h>#define max_size 25int n, count = 1, num[max_size], visited[max_size];int p[40] = {0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0};void dfs(int x) {    if(x > n && p[1 + num[n]]) {        printf("%d", num[1]);        for(int i = 2; i <= n; ++i)            printf(" %d", num[i]);        printf("\n");        return;    }    for(int i = 2; i <= n; ++i) {        if(!visited[i] && p[num[x - 1] + i]) {            visited[i] = 1;            num[x] = i;            dfs(x + 1);            visited[i] = 0;        }    }}int main() {    while(~scanf("%d", &n)) {        memset(visited, 0, sizeof(visited));        num[1] = 1;        printf("Case %d:\n", count++);        if(n % 2 == 0)            dfs(2);        printf("\n");    }}