AOJ-AHU-OJ-460 Prime Ring Problem(栈)

37 篇文章 0 订阅
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.

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
OriginalTransformed
6
8

Sample Output
OriginalTransformed
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的和也是素数。怎么处理呢?数数就可以。
代码如下:
栈:
#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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值