Prime Ring Problem
Time Limit: 15000 ms Memory Limit: 64 MB
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.
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.
You are to write a program that completes above process.
Print a blank line after each case.
Sample Input
Original | Transformed |
6 8
Sample Output
Original | Transformed |
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
Source
Asia 1996, Shanghai (Mainland China).
——————————————————————分割线——————————————————————
思路:此题难度比较大一点。牵扯到两个问题,一个是环,一个是DFS。填满一个环,使环上相邻两数之和为素数。
首先把环拆开,只考虑DFS的问题。从1开始,向下遍历。从2~n之间选一个数,假设选择了i1,之后从i1向下遍历……直到将n全部遍历。很裸的一个DFS。当一条路可以走,那么一直走到死,之后回到上一个结点,走没走过的路。所有路走完以后,就可以输出需要的解了。我们先用栈来模拟这个过程。结点的访问有一个问题:假设n=6,我的路径
I:1->2->3->4->5(X)
II:1->2->3->4->6(X)
III:1->2->3->5(X)
IV:1->2->3->6(X)
V:1->2->5->
很明显,从 I 到 IV 我对结点3、4做了访问标记,但是都走不通。到了 V 从结点5向下遍历时,结点3、4不管是否成立,我都有必要做一下判断,访问标记必须清空。但是清空之后,回到栈首遍历时,不可以再次陷入该结点。我们设置一个die变量,避免访问这个走不通的结点。最后,通过设置die变量的方法解决访问问题之后,解决环的问题。要成环,则必须最后一个数字和1的和也是素数。怎么处理呢?数数就可以。
代码如下:
栈:
——————————————————————分割线——————————————————————
思路:此题难度比较大一点。牵扯到两个问题,一个是环,一个是DFS。填满一个环,使环上相邻两数之和为素数。
首先把环拆开,只考虑DFS的问题。从1开始,向下遍历。从2~n之间选一个数,假设选择了i1,之后从i1向下遍历……直到将n全部遍历。很裸的一个DFS。当一条路可以走,那么一直走到死,之后回到上一个结点,走没走过的路。所有路走完以后,就可以输出需要的解了。我们先用栈来模拟这个过程。结点的访问有一个问题:假设n=6,我的路径
I:1->2->3->4->5(X)
II:1->2->3->4->6(X)
III:1->2->3->5(X)
IV:1->2->3->6(X)
V:1->2->5->
很明显,从 I 到 IV 我对结点3、4做了访问标记,但是都走不通。到了 V 从结点5向下遍历时,结点3、4不管是否成立,我都有必要做一下判断,访问标记必须清空。但是清空之后,回到栈首遍历时,不可以再次陷入该结点。我们设置一个die变量,避免访问这个走不通的结点。最后,通过设置die变量的方法解决访问问题之后,解决环的问题。要成环,则必须最后一个数字和1的和也是素数。怎么处理呢?数数就可以。
代码如下:
栈:
#include <stdio.h>
#include <memory.h>
int n, cou;
int vis[20];
int sta[20];//sta数组作为栈
int su[40];//40以内的素数问题犯不着写函数判断,打表就行了
void dfs(int j){
int top = -1, die = 1, a;//top指针相当重要,初始化为-1。die变量是“清除”访问的小技巧
int u = j, k;
sta[++top] = u;//入栈
vis[u] = 1;
cou++;//栈内多了一个元素。(cou我初始化为1,因为我假装1在栈内,其实栈内并没有1)
while(top != -1){//栈为空之前,一直循环
for(k = die+1; k <= n; k++){//从上一个走不通的结点处的下一个结点开始,这就利用die避免了对die的访问
die = 1;//die一旦起了作用,就要初始化成1。这样下一次没有走死的话,就可以从2开始遍历
if(su[u+k] && !vis[k]){//是素数,且不曾访问
vis[k] = 1;
u = sta[++top] = k;//入栈
cou++;
break;//这条路能走通!那么走到走不通为止
}
}
if(k > n){//判断何时走不通。1~n都试了,就是走不通了
if(cou == n){//如果走不通的时候,环已经填满,那么判断首尾和是否也是素数
if(su[u+1])
cou++;
if(cou == n+1){//是的话,打印
printf("1 ");
for(a = 0; a < n-2; a++)
printf("%d ", sta[a]);
printf("%d\n", sta[a]);
cou--;//一定要记得!因为你利用cou判断首尾相接能否成功,所以你多数了一个!
}
}
die = sta[top];//不管有没有打印,总归路是死在栈首了
vis[die] = 0;//清空访问标记
--top;//出栈
if(top == -1)
return ;//出栈以后,栈空了,回城吧
u = sta[top];//u更新为当前栈首元素
cou--;//栈内少了一个
}
}
}
int main(){
int k, i, cas = 0;
su[2] = su[3] = su[5] = su[7] = su[11] = su[13] = su[17] = su[19] = 1;
su[23] = su[29] = su[31] = su[37] = 1;//40以内素数打表
while(scanf("%d", &n) != EOF){
printf("Case %d:\n", ++cas);
if(su[n]&&n != 2){//此处直到下一个注释处为优化部分
printf("\n");continue;
}
else if(n == 9){
printf("\n");continue;
}
else if(n == 15){
printf("\n");continue;
}//经过一次成功的执行之后,发现3,5,7,9,11,13,15,17,19这些数是无解的,不必浪费时间,直接输出空行
memset(vis, 0, sizeof(vis));//初始化所有访问标记
for(i = 2; i <= n; i++){
cou = 1;
if(su[1+i] && !vis[i]){
dfs(i);//从2~n进行DFS简单深搜
}
}
printf("\n");
}
return 0;
}