题目:http://acm.hdu.edu.cn/showproblem.php?pid=1016
题目大意:输入一个数字n,输出1到n所有数字组成的序列,每个序列中每个数字出现有且仅有一次。相邻两个数字的和为素数,最后一个数字和第一个数字的和为素数。按照字典序来输出所有可能的结果。所有可能的结果都要以1开头
首先,本题中如果调用STL中的next_permutation方法,随后遍历整个序列判断是否满足题意这种做法来做的话,是会超时的。那么超时的原因在哪里呢?比如,以n=4为例吧,对于排列1 3 2 4这个是不满足题意的,因为1和3的和是4。那么如果直接调用next_permutation方法得到1 3 4 2,相当于1和3依旧相邻,这个时候如果再次遍历整个序列的话,就相当于进行了多余的计算。所以我的思路是能不能避免这种重复,比如1 3 4 2这个序列1 和3 已经相邻了,之后不让它们两个再次相邻。
按照这个思路,我设计了一个算法,首先,对于1到4的任意一个数k,列出所有能和k相邻的数,对于任意一个数k,设置一个指针,指向能和k相邻的某一个数。例如:
1:2,4 指针p1初始指向2之前一个位置。
2:1,3 指针p2初始指向1之前的一个位置。
3:2,4 指针p3初始指向2之前的一个位置。
4:1,3 指针p4初始指向1之前的一个位置。
注意可以和k相邻的数字按照有小到大排序,这样保证输出结果字典序递增。
设定一个双端队列,进行DFS,运行流程为:
1入队列尾端
当前数字为1,p1++指向2,2入队列尾端
当前数字为2,p2++指向1,1已经进入队列,p2++指向3,3入队列尾端
当前数字为3,p3++指向2,2已经进入队列,p3++指向4,4没有入队列,但此时队列中已经有3个元素了(n=4,3=n-1),验证一下4加队首元素1为素数,此时4进入队列尾端。
此时队列中已经有4个元素了,输出一种排列1 2 3 4。
4出队列,当前数字为队尾元素3,p3++指向4后面的一个位置,此时3出队列,p3恢复到初始位置。
当前数字为队尾元素2,p2++指向3后面的一个位置,2出队列,p2恢复到初始位置。
当前数字为队尾元素1,p1++指向4,4入队列尾端
当前数字为4,p4连加两次指向3(1已经入队列),3入队列尾端。
当前数字为3,p3++指向2,此时队列中有三个元素,验证一下2+队首元素1为素数,2入队列。
此时队列中已经有四个元素了,输出1 4 3 2
这个用到的也是DFS的思想,但是计算过程大大简化。后来看答案发现好像按照经典的DFS递归地求字典序递增的排列也不会超时,但是这种非递归的程序所用的时间会更短一些。
程序实现中,用一个类Adjacent来封装相邻数字。Adjacent类中num表示数字,adjacent表示能和num相邻的数字个数,all_num存储所有能和num相邻的数字,从下标0开始存储,pointer为上例中的p1,p2,p3,p4。sign[i]表示i是否在队列中。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<deque>
using namespace std;
int n;
//记录每个数字可以相邻的数字集合
class Adjacent{
public:
int num;//当前数字
int adjacent;//可以和num相邻的数字个数
int all_num[25];//all_num[0]-all_num[adjacent-1]存储所有可以和num相邻的数字
int pointer;//指向当前元素
Adjacent(){}
void init(int value);
bool can_adjacent(int a,int b);//判断a和b能否相邻
};
void Adjacent::init(int value){
num=value;
adjacent=0;
for(int i=1;i<=n;i++){
if(i==num) continue;
if(can_adjacent(i,num)){
all_num[adjacent++]=i;
}
}
pointer=-1;//pointer在起始位置
}
bool Adjacent::can_adjacent(int a,int b){
int sum=a+b;
for(int i=2;i<=(int)sqrt(sum);i++){
if(sum%i==0) return false;
}
return true;
}
Adjacent a[25];
bool sign[25];//sign[i]标识i是否入栈
int main(){
deque<int> q;
int case_num=0;
while(cin>>n){
int _back;//队列尾部元素
if(n==1){
cout<<"Case "<<++case_num<<":"<<endl;
cout<<"1"<<endl<<endl;
continue;
}else{
cout<<"Case "<<++case_num<<":"<<endl;
}
for(int i=1;i<=n;i++) a[i].init(i);
q.clear();
#ifdef DEBUG
cout<<"1 in deque"<<endl;
#endif
q.push_back(1);
sign[1]=true;
_back=1;
for(int i=2;i<=n;i++) sign[i]=false;//初始化,初始只有1入队列了
while(a[1].pointer<a[1].adjacent){
a[_back].pointer++;
int data;
while(a[_back].pointer<a[_back].adjacent){
data=a[_back].all_num[ a[_back].pointer];
if(!sign[data]) break;
a[_back].pointer++;
}
if(a[_back].pointer>=a[_back].adjacent){
//_back出队列
#ifdef DEBUG
cout<<_back<<"out deque"<<endl;
#endif
a[_back].pointer=-1;
sign[_back]=false;
q.pop_back();
if(_back==1) break;
_back=q.back();
continue;
}
if(q.size()==(n-1)){
if(a[data].can_adjacent(data,1)){
#ifdef DEBUG
cout<<data<<"in deque"<<endl;
#endif
q.push_back(data);
sign[data]=true;
_back=data;
}else{
a[data].pointer=-1;
sign[data]=false;
a[_back].pointer=-1;
sign[_back]=false;
#ifdef DEBUG
cout<<_back<<"out deque"<<endl;
#endif
q.pop_back();
if(_back==1) break;
_back=q.back();
continue;
}
}else{
#ifdef DEBUG
cout<<data<<"in deque"<<endl;
#endif
q.push_back(data);
sign[data]=true;
_back=data;
}
//输出一个结果
if(q.size()==n){
deque<int>::iterator iter;
for(iter=q.begin();iter!=q.end()-1;iter++){
cout<<*iter<<" ";
}
cout<<*iter<<endl;
sign[_back]=false;
#ifdef DEBUG
cout<<_back<<"out deque"<<endl;
#endif
q.pop_back();
if(_back==1) break;
_back=q.back();
}
}
cout<<endl;
}
}