nyoj 488 素数环(深搜+剪枝)

xiaoxiao2021-02-28  34

题目来源:http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=488

素数环

时间限制:1000 ms |  内存限制:65535 KB

难度:2

描述

有一个整数n,把从1n的数字无重复的排列成环,且使每相邻两个数(包括首尾)的和都为素数,称为素数环。

为了简便起见,我们规定每个素数环都从1开始。例如,下图就是6的一个素数环。

输入

有多组测试数据,每组输入一个n(0<n<20)n=0表示输入结束。

输出

每组第一行输出对应的Case序号,从1开始。如果存在满足题意叙述的素数环,从小到大输出。否则输出NoAnswer

样例输入

6830

样例输出

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 2Case 3:No Answer

 -----------------------------------------------------

思路

深搜+剪枝。

递归深搜所有可能的排列,逐一判定。关键在于发现性质:如果n为奇数,则一定不能组成素数环(这是因为奇数个1~n中奇数多于偶数,必定存在两个不相同的奇数相邻,两个不相同的奇数之和一定是合数)。如果没有发现这条的话就会超时。

-----------------------------------------------------

代码

#include<iostream> #include<cstring> using namespace std; int n; int a[25] = {}; // 环上的数的序列 const bool prime[25] = {0,1,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0}; // 0~24质数表,是质数为1,不是质数为0 bool vis[25] = {}; // 是否在环上用过这个数 bool has_ans = false; // 一个Case是否有解判断标志 void dfs(int cnt) // 深搜输出可行解,cnt: 当前正在搜索环上第cnt个节点 { int i; if (cnt==n) // 如果环已经放满了 { if (prime[a[n-1]+1]) // 判断最后一个数与第一个数之和是不是质数 { has_ans = true; // 找到解 for (i=0; i<n; i++) // 直接输出 { cout << a[i] << " "; } cout << endl; } return; // 环填完了,函数返回 } if (cnt==0) // 填第一个数 { a[cnt] = 1; // 第一个填的一定是1 vis[1] = 1; dfs(1); // 递归深搜 } else { for (i=1; i<=n; i++) // 遍历所有可能的n个数 { if (!vis[i] && prime[a[cnt-1]+i]) // 如果环上没有用过这个数且它与前序节点之和为质数 { a[cnt] = i; // 把这个数放到环上 vis[i] = 1; // 访问标记置1 dfs(cnt+1); // 递归深搜 vis[i] = 0; // 再把标记清除方便下一个分支的深搜 } } } } int main() { int t = 1; // Case ID while (cin >> n) { if (n==0) { break; } memset(vis,0,sizeof(vis)); // 清空访问标记 cout << "Case " << (t++) << ":" << endl; if (n==1) // n=1的情况特殊处理(此题把1也当做质数) { cout << 1 << endl; continue; } if (n%2==1) // 如果是奇数,则环上不可能一奇一偶,必然有两不同奇数相邻,它们的和必定是合数 { cout << "No Answer" << endl; // 排除奇数的情况是不超时的关键 continue; } has_ans = false; // 将找到可行解标志位置0 dfs(0); // 从环上第一个点开始深搜 if (!has_ans) { cout << "No Answer" << endl; } } return 0; }
转载请注明原文地址: https://www.6miu.com/read-2050254.html

最新回复(0)