HDOJ1016 非递归DFS

题目: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;

  }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值