nyist-素数环

素数环

时间限制: 1000 ms  |  内存限制: 65535 KB
难度: 2
描述

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

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

输入
有多组测试数据,每组输入一个n(0<n<20),n=0表示输入结束。
输出
每组第一行输出对应的Case序号,从1开始。
如果存在满足题意叙述的素数环,从小到大输出。
否则输出No Answer。
样例输入
6
8
3
0
样例输出
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
Case 3:
No Answer


开始一上来的思路就是列出所有情况~~~啥都不管直接找

但是想想    6个数组成的6位数  有  6!个

n=20 时  20!一个一个找 ?能不超时?

剪枝,把两边和不是素数去掉  合理  可以

好了 经过我认真分析发现素数和跟我写的zb和 部分和 有一点很大的不同

比如我在部分和中   发现这个数不行 溯回后 再向下查找时  这个数后面就不用了

比如例子   

3  21

5 8 13

第一遍  找了  5 8 13  发现不行  回溯  到8 还不行  回溯到  5  还不行回溯  从8开始(8为第一个数)  

后面就不用5了

而这个素数和不一样,接下来的搜索 还我要用  还要思考如何在后面继续用到这个数

那我用过就把这个数标记了,如果回溯就取消标记

for循环的时候从头循环

for(int i=pos; i<n; i++)  
    {  
        sum+=a[i];  
        visit[i]=1;  
        /*printf("%d %d %d\n",pos,i,sum); 
        for(int k=0; k<n; k++) 
        { 
            printf("%d ",visit[k]); 
        } 
        printf("\n");*/  
        dfs(i+1);  
        sum-=a[i];  
        visit[i]=0;  
    }  

这是部分和问题的中的for循环  从当前递归的数 的下一位开始   前面的数都不用了

好了  素数环要改变策略了  


#include<stdio.h>
#include<math.h>
#include<string.h>
int a[30],b[30];
int n,j;
bool flag;
/*int ss(int x)
{
    int l=1;
    if(x==0||x==1)
    {
        x=0;
    }
    for(int i=2; i<=sqrt(x); i++)
    {
        if(x%i==0)
        {
            l=0;
        }
    }
    return l;
}*/
void dfs(int x)
{
    /*if(ss(a[j]+a[j-1])!=1)
    {
           return;                                                                                                                                        
    }*/
    if(x>n)
    {
        for(int i=1; i<=n; i++)
        {
            printf("%d ",b[i]);
        }
        printf("\n");
        return ;
    }
    for(int i=1; i<n; i++)
    {
        if(a[i]==0)
        {
            printf("%d$\n",x);
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=x; //将当期数存入b中
            for(int i=1; i<=n; i++)
            {
                printf("%d ",b[i]);
            }
            printf("*\n");
            dfs(i+1);
            b[--j]=0;
            a[i]=0;     //去掉标记
            for(int i=1; i<=n; i++)
            {
                printf("%d ",b[i]);
            }
            printf("**\n");
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
        {
            break;
        }
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        flag=false;
        j=1;
        dfs(1);
    }
}


好了  我写的代码  输出没结果  尴尬

这样  一步一步来  先来输出所有的情况 再判断那种符合条件

那咱们先找出所有的情况  其他的不考虑

简单化 

简单化的过程中发现了自己的一个思维错误

(这就是自己dbug的好处)

如果这样写递归 永远也出不了结果

为什么那

我输入3

递归是从第二个数  2 开始的

进去后发现到了x=2  就return  了

虽然我都没用return 

过程中根本就没输出结果

改了一下

 for(int i=1; i<=n; i++)
    {
        if(a[i]==0)
        {
            printf("%d$\n",x);
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=x; //将当期数存入b中

发现有一种情况可以输出  但是另外的情况的最后的一个数都输不出来

推理一下  可能是回溯的问题

又修改了一个地方  原来的

dfs(i+1)
改为
dfs(x+1)
好了   就可以输出所有排序情况

代码

#include<stdio.h>
#include<math.h>
#include<string.h>
int a[30],b[30];
int n,j;
bool flag;
/*int ss(int x)
{
    int l=1;
    if(x==0||x==1)
    {
        x=0;
    }
    for(int i=2; i<=sqrt(x); i++)
    {
        if(x%i==0)
        {
            l=0;
        }
    }
    return l;
}*/
void dfs(int x)
{
    /*if(ss(a[j]+a[j-1])!=1)
    {
           return;                                                                                                                                        
    }*/
    if(x>n)
    {
        for(int i=1; i<=n; i++)
        {
            printf("%d ",b[i]);
        }
        printf("\n");
        return ;
    }
    for(int i=1; i<=n; i++)
    {
        if(a[i]==0)
        {
            //printf("%d$\n",x);
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=i; //将当期数存入b中
            /*for(int i=1; i<=n; i++)
            {
                printf("%d ",a[i]);
            }
            printf("*\n");*/
            dfs(x+1);
            b[--j]=0;
            a[i]=0;     //去掉标记
            /*for(int i=1; i<=n; i++)
            {
                printf("%d ",a[i]);
            }
            printf("**\n");*/
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
        {
            break;
        }
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        //flag=false;
        j=1;
        dfs(1);
    }
}
纯手工  自己想 ,自己dbug  真不容易!!!!!

分析一下为什么这样改  还有为什么会在2时候就return

如果  是 dfs(i+1)的话如果我第二个数选3  即选了  1 3  

本来该选3  了  可是我dfs(i+1) 就是dfs(4) 就符合return条件了

直接跳出

下面添枝加叶就完成这道题了


#include<stdio.h>
#include<math.h>
#include<string.h>
int a[30],b[30];
int n,j;
bool flag;
int ss(int x)
{
    int l=1;
    if(x==0||x==1)
    {
        x=0;
    }
    for(int i=2; i<=sqrt(x); i++)
    {
        if(x%i==0)
        {
            l=0;
        }
    }
    return l;
}
void dfs(int x)
{
    if(a[1]!=1)
    {
        return;
    }
    if(ss(a[j]+a[j-1])!=1&&j>1)
    {
           return;                                                                                                                                       
    }
    if(x>n)
    {
        flag=true;
        for(int i=1; i<=n; i++)
        {
            printf("%d ",b[i]);
        }
        printf("\n");
        return ;
    }
    for(int i=1; i<=n; i++)
    {
        if(a[i]==0)
        {
            //printf("%d$\n",x);
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=i; //将当期数存入b中
            /*for(int i=1; i<=n; i++)
            {
                printf("%d ",a[i]);
            }
            printf("*\n");*/
            dfs(x+1);
            b[--j]=0;
            a[i]=0;     //去掉标记
            /*for(int i=1; i<=n; i++)
            {
                printf("%d ",a[i]);
            }
            printf("**\n");*/
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if(n==0)
        {
            break;
        }
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        flag=false;
        j=1;
        dfs(1);
        if(!flag)
        {
            printf("No Answer\n");
        }
    }
}

输出的全是No Answer

无语...........

发现了

 if(a[1]!=1)
    {
        return;
    }

这个判断语句是有毛病的  我第一次进来

直接就return回去了  因为  a【1】还是0那   直接我就不递归了  那不就WA了

修改后代码

#include<stdio.h>
#include<math.h>
#include<string.h>
int a[30],b[30];
int n,j,step=0,time;
bool flag;
int jude(int a)
{
    int p=1;
    if(a==1||a==0)
    {
        return 0;
    }
    for(int i=2; i<=sqrt(a); i++)
    {
        if(a%i==0)
        {
            p=0;
        }
    }
    return p;
}
void dfs(int x)
{
    if(j>=2&&jude(b[j-1]+b[j-2])!=1)
    {
        return;
    }
    if(x>=n&&jude(b[n-1]+1)!=1)
    {
        return;
    }
    if(x>=n)
    {
        if(time==1)
        {
            printf("Case %d:\n",step);
            time++;
        }
        for(int i=0; i<n; i++)
        {
            printf("%d ",b[i]);
        }
        printf("\n");
        flag=true;
        return ;
    }
    for(int i=2; i<=n; i++)
    {
        if(a[i]==0)
        {
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=i; //将当期数存入b中
            dfs(x+1);
            b[--j]=0;
            a[i]=0;     //去掉标记
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        step++;
        if(n==0)
        {
            break;
        }
        memset(a,0,sizeof(a));
        b[0]=1,b[1]=2;
        j=1,time=1;
        flag=false;
        dfs(1);
        if(!flag)
        {
            printf("Case %d:\n",step);
            printf("No Answer\n");
        }
    }
    return 0;
}

万事OK   提交

TL  超时!!

好了   发现一个规律  

凡是奇数的  都是  no answer

因为奇数个数的数 组成环  必须会有2个奇数组合在一起

奇数加奇数为偶数  直接就不是素数了

再提交   

猝不及防  一个WA   

因为   当n=1时  应该输出1  

这输出的  no answer

╮(╯▽╰)╭

AC代码

#include<stdio.h>
#include<math.h>
#include<string.h>
int a[30],b[30];
int n,j,step=0,time;
bool flag;
int jude(int a)
{
    int p=1;
    if(a==1||a==0)
    {
        return 0;
    }
    for(int i=2; i<=sqrt(a); i++)
    {
        if(a%i==0)
        {
            p=0;
        }
    }
    return p;
}
void dfs(int x)
{
    if(j>=2&&jude(b[j-1]+b[j-2])!=1)
    {
        return;
    }
    if(x>=n&&jude(b[n-1]+1)!=1)
    {
        return;
    }
    if(x>=n)
    {
        if(time==1)
        {
            printf("Case %d:\n",step);
            time++;
        }
        for(int i=0; i<n; i++)
        {
            printf("%d ",b[i]);
        }
        printf("\n");
        flag=true;
        return ;
    }
    for(int i=2; i<=n; i++)
    {
        if(a[i]==0)
        {
            a[i]=1;    //  标记 表示这个数用过
            b[j++]=i; //将当期数存入b中
            dfs(x+1);
            b[--j]=0;
            a[i]=0;     //去掉标记
        }
    }
}
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        step++;
        if(n==0)
        {
            break;
        }
        if(n==1)
        {
            printf("Case %d:\n",step);
            printf("1\n");
            continue;
        }
        if(n%2==1)
        {
            printf("Case %d:\n",step);
            printf("No Answer\n");
            continue;
        }

        memset(a,0,sizeof(a));
        b[0]=1,b[1]=2;
        j=1,time=1;
        flag=false;
        dfs(1);
        if(!flag)
        {
            printf("Case %d:\n",step);
            printf("No Answer\n");
        }
    }
    return 0;
}


总结   

小白我 明白了  memset  只能把数组重置为  0 或者  -1  其他的都是乱码

还有  做题  从基础的开始  

一道题也许对你很难

那你一步一步实现

就如这一题  

先找出所有情况,再排除一些













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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值