6 8
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
此题用的方法巧妙,深搜加剪枝,如果输入N是奇数的话,由于起点从1开始,那么1-N之间一共有N / 2个偶数,N / 2 + 1个奇数,也就是奇数的个数比偶数多一个,那么把这N个数排成一个环,根据鸽巢原理,必然有两个奇数是相邻的,而两个奇数之和是偶数,偶数不是素数,所以我们得出结论,如果输入N是奇数的话,没有满足条件的排列。这样当N是奇数的时候,直接返回可。如果1-N之间每个数输入的几率相同,这个判断可以减少一半的计算量。再扩展一下,可以发现,任何一个满足条件的排列都有一个共同点:相邻的两个数奇偶性必然不同,原因是:两个奇数之和或者两个偶数之和都是偶数,而偶数一定不是素数,所以在选取当前元素的时候,比较一下它和前一个元素的奇偶性。再做决定,可以减少一部分计算量。由 于奇数 + 偶数 = 奇数, 而奇数的二进制表示中,最低位是1, 所以有下面的代码, 其中curValue是当前值, a[lastIndex]是前一个值.
#include<stdio.h> #include<stdlib.h> int prime[38]{ //巧妙的应用 0,0,1,1,0,1,0,1,0, 0,0,1,0,1,0,0,0,1, 0,1,0,0,0,1,0,0,0, 1,0,1,0,1,0,0,0,0, 0,1 }; bool isok(int a[], int lastIndex, int curValue){ //if( lastIndex < 0 )//完全没必要判断,因为不可能小于零 // return true; if(!prime[a[lastIndex]+curValue]) //判断相邻两个数的和是否为素数 return false; if(!((a[lastIndex]+curValue)&1)) //判断相邻两个数的奇偶性 return false; for(int i=0; i<=lastIndex; ++i){ //判重 if(a[i]==curValue) return false; } return true; } void output(int a[], int n){ for(int i=0; i<=n-1; ++i){ if(i==n-1) printf("%d",a[i]); else printf("%d ",a[i]); } printf("\n"); } void cyclePrime(int a[], int n, int k){ if(n&1) return; if(k==n){ if(prime[a[0]+a[n-1]]) //并且首尾也满足相加为素数,输出 output(a,n); } else{ for(int i=2;i<=n;++i){ a[k]=i; if(isok(a,k-1,i)) cyclePrime(a,n,k+1); } } } int main(){ int n,m=0; while(scanf("%d",&n)==1){ int a[25]; a[0]=1; printf("Case %d:\n",++m); cyclePrime(a,n,1); printf("\n"); } return 0; }