【大意】
给定数n(n<20),在n的所有排列中,记a[1],a]2],...,a[n],求满足a[i]+a[i+1](1<=i<n)是素数并且a[1]+a[n]也是素数的所有排列。排列按字典序输出。
【分析】
经典的搜索问题。
先搜索19+18以内的素数,记vis[i]:true表示i是合数,false表示是质数
记can[i][j]:true表示i+j是素数,false表示是合数
顺序从小到大搜索即可,注意相邻2个数必然奇偶不同,这样搜索到的结果满足字典序。
由于是素数环要求,可以双向搜索,但结果不一定是字典序,可用set保存。
【参考代码】
#include <string.h> #include <stdio.h> #include <set> #include <ctype.h> #include <algorithm> #include <queue> #include <string.h> using namespace std; const int maxn = 64 ; int ans[maxn] ; bool can[maxn][maxn] , vis[maxn] ; inline bool get(int &t) { bool flag = 0 ; char c; while(!isdigit(c = getchar())&&c!='-') if( c == -1 ) break ; if( c == -1 ) return 0 ; if(c=='-') flag = 1 , t = 0 ; else t = c ^ 48; while(isdigit(c = getchar())) t = (t << 1) + (t << 3) + (c ^ 48) ; if(flag) t = -t ; return 1 ; } int n ; void init() { int i , j ; for( i = 2 ; i < maxn ; i++) if(!vis[i]) { for( j = i*i ; j < maxn ; j += i ) vis[j] = 1 ; } for( i = 1 ; i <= 20 ; i++) for( j = i+1 ; j <= 20 ; j++) if(!vis[i+j]) can[i][j] = can[j][i] = 1 ; } void dfs(int pos) { int i ; if( pos == n ) { if(!can[1][ans[n-1]]) return ; printf("1"); for( i = 1 ; i < n ; i++) printf(" %d",ans[i]); puts(""); } else { i = ( pos & 1 ) ? 2 : 3 ; for( ; i <= n ; i += 2 ) if(!vis[i]&&can[ans[pos-1]][i]) { ans[pos] = i ; vis[i] = 1 ; dfs(pos+1); vis[i] = 0 ; } } } void solve() { memset(ans,0,sizeof(ans)); memset(vis,0,sizeof(vis)); ans[0] = 1 ; vis[1] = 1 ; dfs(1); } int main() { int i ; init(); for( i = 1 ; get(n) ; i++) { printf("Case %d:\n",i); solve(); puts(""); } }