1. 程式人生 > >回溯法之遞歸回溯和迭代回溯

回溯法之遞歸回溯和迭代回溯

/* 
    設R={r1,r2,...rn}是要進行排列的n個元素.Ri=R-{ri}.集合X中元素的全排列記為 
    Perm(X).(ri)Perm(X)表示在全排列Perm(X)的每一個排列前加上字首ri得到的排列 
    R的全排列可歸納定義如下: 
        當n=1時,Perm(R)=(r),其中r是集合R中唯一的元素; 
        當r>1時,Perm(R)由(r1)Perm(r1),(r2)Perm(r2).....(rn)Perm(rn)構成. 
        依此遞迴定義,Perm(R)的遞迴演算法如下: 
*/
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int cnt = 0;
void Perm(int list[],int k,int m){
  if(k == m){
    cnt++;
    cout<<"cnt = "<<cnt<<endl;
    for(int i = 0 ; i <= m ; i++){
      cout<<list[i]<<"  ";
    }
    cout<<endl;
  }
  else{
    for(int i = k ; i <= m; i++){
      swap(list[k],list[i]);
      Perm(list,k+1,m);
      swap(list[k],list[i]);
    }
  }

}
/*
迭代實現,演算法思想:
該問題的解空間是一棵排列樹(可以看作子集樹剪去了很多枝)
因此採用回溯法對解空間樹進行搜尋,得到想要的結果
對於長度為N的序列,可以看作深度為N的一棵樹,依次對每一層搜尋其子樹節點
該例子中,第一層可選的子樹節點有N個,第二層有N-1個知道最後一層葉子節點只有一個
是一棵典型的排列樹
*/
void Perm_iterative(int list[],int n){
  int t = 0;
  vector<int> p = {-1,-1,-1,-1,-1};//p[i]表示在第i個位置(樹第i層)排list[p[i]],list的第p[i]個元素
  //set<int> bkt[n+1];
  while(t >= 0){
    int k = p[t];
    while(find(p.begin(),p.end(),k) != p.end())  k++;

    if( k > n || t > n){//返回上一層節點時,清空子樹狀態
        for(int i = t; i <= n; ++i)  p[i] = -1;
        t--;
        continue;
    }
    p[t] = k;
    if( t >= n){//找到一個排列
        cnt++;
        cout<<"cnt = "<<cnt<<endl;
        for(int i = 0 ; i <= n ; i++){
          cout<<list[p[i]]<<"  ";
        }
        cout<<endl;
    }
    else{
        //cout<<"that"<<endl;
        t++;
    }
  }
  return;
}

int main(){
  int x[] = {1,2,3,4,5};
  cout<<"this is  a test"<<endl;
  //Perm(x,0,4);
  Perm_iterative(x,4);
}