素数环
-
描述
-
有一个整数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 其他的都是乱码
还有 做题 从基础的开始
一道题也许对你很难
那你一步一步实现
就如这一题
先找出所有情况,再排除一些