hdu 1016 Prime Ring Problem

解决一个特定问题:构造由1到N的数字组成的环形排列,使得任意相邻两数之和为素数。通过递归回溯算法实现,输出所有符合条件的排列方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
  
  
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, 然后要令这些数字练成一个圆圈, 并且要相邻两个数字的和是一个素数,将所有情况依次输出,并且都是从 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 25
int 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");
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值