HDU1016 Prime Ring Problem【DFS+素数问题】

1.理解什么是素数,只能被1和它本身整除的数叫素数。

bool isPrime(int m)    //求m是不是素数
{    
    for ( int i = 2;i<=(int)sqrt(m);i++)
    {
        if(m % i==0 ) return false;
    }
    return true;
}

2.DFS深度优先搜索。【比如我们要求n = 4个的素数环。】

1是必选项,然后开始遍历其他。根据要求,它是按照字典序来排序,所以应该找 没有选择过的,且最小的数 。即从头开始遍历,找与前一项的和是素数。当最后一项(4个素数换即第4项时),如果条件符合还要看第4个数是不是与第1个数(即1)的和是素数,如果与要求正确。

①。首先是找的2,与前一项相加。2+1 =3,是素数,满足要求、

②。然后找的3,3+2 =  5是素数。满足要求。

③。然后找的4,4+3 = 7是素数,这是第一个条件。同时4=n,是最后一项,还要对与第1项(即1)的和判断是否为素数,这是第二个条件。如果这两个条件都满足才是一个完整的素数环。

4+3 = 7,是素数,4+1 = 5是素数。因此,1,2,3,4是一个素数环。

3.跟着代码走一遍:

 1.创建数组 str[22],vis[22]。其中str数组用来存放保存好的路径信息,vis数组用来表示是否访问过第 i 个点。

创建数组n用来表示有n个元素的素数环。

int str[22];
int vis[22];
int n;

2.主函数走起来,先对str数组和vis数组初始化,然后输入n:

int main(){

    //Initialize:

    for(int i = 2;i<=n;i++)
    {
        str[i] = 0;
        vis[i] = 0;    //vis=0表示没有被访问过
    }
    str[1] = 1;    //第一步必须走1,根据题目要求,环必须从1开始
    vis[1] = 1;    //既然第一步走过了,纳闷他必然访问过了。


}

 3同时我们发现找下一步是个深搜递归的过程,如果可以找到就一直往下找,如果找到全部就输出结果,否则返回上一步,直到所有可能的结果都走遍。

void dfs(int step){    //表示我已经走了step步了
    //1.如果已经走了n步了,那么就要判断第n位(str[n])与1位(1)的和是否为素数,如果是那么就输出整个结果
    if(step == n){
        if(isPrime(str[n] + 1)){
            for(int i=1;i<=n;i++){
                printf("%d%c",str[i],(i<n)?' ':'\n');
            }
        }
    return;
        
    }

    //2剩下的情况就是还没走完,我们就要继续走下去,从小到大遍历所有的点,如果满足 该点没访问过 且 该点与保存好的数组str的最后一项的和是素数,那么久保存这个点,并访问下一个节点。

    for(int i=2;i<=n;i++){
        if(!vis[i] && isPrime(i+str[n]){
            str[n+1] = i;    //保存这个点
            vis[n+1] = 1;    //标记这个点已经被访问过
            dfs(n+1);        //已经走了n+1步,进入找下一个点的过程中。
            vis[n+1] = 0;    //这一步大家可能不太容易理解。比如第2步走完了,发现后面的点都行不通,则要返回上一步继续走,这时候就要把vis数组重新置为0,保证后面可以再次访问到该点
    }

}

 5.所以刚才的主函数中只要再添加一行就可以啦,即

dfs(1);    //表示已经访问了1个节点。即节点1.

完整代码如下:(HDU1016素数环)

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;

int n;
int str[22];
int vis[22];

bool isPrime(int m){
    for(int i=2;i<=(int)sqrt(m);i++){
        if(m%i==0)return false;
    }
    return true;
}

void dfs(int step){ //表示已经走了step步后怎么走
    if(step == n && isPrime(str[n]+1)){
        for(int i=1;i<=n;i++){
            printf("%d%c",str[i],(i<n)?' ':'\n');
        }
        return;    //后面的语句就不用访问了
    }

    for(int i=2;i<=n;i++){
        if(!vis[i] && isPrime(i+str[step])){
            str[step+1] = i;
            vis[i] = 1;
            dfs(step+1);
            vis[i] = 0;
        }
    }

}

int main(){
    int kase = 0;
    while(cin >> n){
        for(int i=2;i<=n;i++){
            str[i] = 0;
            vis[i] = 0;
        }
        str[1] = 1;
        vis[1] = 1;

        cout << "Case " << ++kase << ":"<< endl;
        dfs(1);
        cout << endl;

    }
}

6.总结:做了n次才做出来,费了很长时间。希望不要操之过急,稳扎稳打。

7.补充:我第一次Presentation Error了,原因是我写的是前几个case都有空格,最后一个没有。他的格式有些不一样,是每一行都要有一个空格。不要在意细节,主要看懂思想和方法。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值