这次又有一点小的修改,主要是关于StateSpaceTreeSearch的第二个模板参数的。它是用于指定选用BFS还是DFS,原来我是让使用者选择BFSInserter和DFSInserter两者之一。后来想想,好象不应该让用户看到这两个结点插入策略,毕竟用户并不关心这一点。用户关心的仅是如何在BFS和DFS两者之间选择一个,所以我把选择的方法改为用一个bool常量来指定。
把原来的SearchInserter模板参数改为一个非类型模板参数,命名为UseBFS,当它为true时就用BFS,否则就用DFS。再定义一个SearchInserter模板来根据bool参数选择相应的BFSInserter或DFSInserter,代码如下:
// Filename: BFS_DFS_v1.6.hpp
#ifndef _BFS_DFS_HPP
#define _BFS_DFS_HPP
#include <list>
#include <vector>
#include <set>
#include <ext/hash_set>
using std::list;
using std::vector;
using std::set;
using __gnu_cxx::hash_set;
using __gnu_cxx::hash;
// 仿函式,用于不检查状态结点是否重复
template <class T>
struct NoCheckDup : std::unary_function<T, bool>
{
bool operator() (const T&) const
{
return false;
}
};
// 仿函式,用vector容器检查状态结点是否重复,线性复杂度
// 要求状态类提供operator==
template <class T>
class SequenceCheckDup : std::unary_function<T, bool>
{
typedef vector<T> Cont;
Cont states_;
public:
bool operator() (const T& s)
{
typename Cont::iterator i =
find(states_.begin(), states_.end(), s);
if (i != states_.end()) // 状态已存在,重复
{
return true;
}
states_.push_back(s); // 状态未重复,记录该状态
return false;
}
};
// 仿函式,用set容器检查状态结点是否重复,对数复杂度
// 要求状态类提供operator<
template <class T>
class OrderCheckDup : std::unary_function<T, bool>
{
typedef set<T> Cont;
Cont states_;
public:
bool operator() (const T& s)
{
typename Cont::iterator i = states_.find(s);
if (i != states_.end()) // 状态已存在,重复
{
return true;
}
states_.insert(i, s); // 状态未重复,记录该状态
return false;
}
};
// 仿函式,用hash_set容器检查状态结点是否重复
// 要求状态类提供operator==以及hash()成员函数
template <class T>
class HashCheckDup : std::unary_function<T, bool>
{
struct HashFcn
{
size_t operator()(const T& s) const { return s.hash(); }
};
typedef hash_set<T, HashFcn> Cont;
Cont states_;
public:
HashCheckDup() : states_(100, HashFcn()) {}
bool operator() (const T& s)
{
if (states_.find(s) != states_.end()) // 状态已存在,重复
{
return true;
}
states_.insert(s); // 状态未重复,记录该状态
return false;
}
};
// BFS算法对应的新结点插入策略
template <class Cont>
class BFSInserter
{
public:
BFSInserter(Cont& c) : c_(c) {}
typedef typename Cont::iterator iterator;
typedef typename Cont::value_type value_type;
void operator()(iterator it, const value_type& v) {
c_.push_back(v); //新结点插入到列表的末端,即未搜索的结点后
}
private:
Cont& c_;
};
// DFS算法对应的新结点插入策略
template <class Cont>
class DFSInserter
{
public:
DFSInserter(Cont& c) : c_(c) {}
typedef typename Cont::iterator iterator;
typedef typename Cont::value_type value_type;
void operator()(iterator it, const value_type& v) {
c_.insert(++it, v); //新结点插入到未搜索的结点前
}
private:
Cont& c_;
};
// 状态空间树搜索模板
// State:问题状态类,提供nextStep()和isTarget()成员函数
// UseBFS:可选择BFS或DFS,true为BFS,false为DFS,缺省为BFS
// CheckDup:状态查重算法,可选择NoCheckDup,HashCheckDup,OrderCheckDup等
template < class State, bool UseBFS = true,
template <class> class CheckDup = NoCheckDup >
class StateSpaceTreeSearch
{
template <class Cont, bool>
struct SearchInserter
{
typedef DFSInserter<Cont> Inserter;
};
template <class Cont>
struct SearchInserter<Cont, true>
{
typedef BFSInserter<Cont> Inserter;
};
public:
typedef list<State> Cont;
typedef typename Cont::iterator iterator;
typedef typename SearchInserter<Cont, UseBFS>::Inserter Inserter;
template <class Func>
int operator()(const State& initState, Func afterFindSolution) const
// initState : 初始化状态,类State应提供成员函数nextStep()和isTarget(),
// nextStep()用vector<State>返回下一步可能的所有状态,
// isTarget()用于判断当前状态是否符合要求的答案;
// afterFindSolution : 仿函式,在找到一个有效答案后调用之,它接受一个
// const State&,并返回一个bool值,true表示停止搜索,
// false表示继续搜索
// return : 找到的答案数量
{
CheckDup<State> checkDup;
Cont states;
Inserter inserter(states);
states.push_back(initState);
iterator head = states.begin(); //指向下个搜索的结点
vector<State> nextStates;
int n = 0; //记录找到的解答数量
bool stop = false;
while (!stop && head != states.end())
{
State s = *head; //搜索一个结点
nextStates.clear();
s.nextStep(nextStates); //从搜索点生成下一层结点
for (typename vector<State>::iterator i = nextStates.begin();
i != nextStates.end(); ++i)
{
if (i->isTarget())
{ // 找到一个目标状态
++n;
if (stop = afterFindSolution(*i)) // 处理结果并决定是否停止
{
break;
}
} else { // 不是目标状态,判断是否放入搜索队列中
if (!checkDup(*i)) // 只将不重复的状态放入搜索队列
{
inserter(head, *i);
}
}
}
++head; //指针移到下一个元素
}
return n;
}
};
#endif
相应地,使用者的代码也有所改变,以推箱子为例,如下:
// soko.cpp -- 推箱子主程序
#include <iostream>
#include "BFS_DFS_v1.6.hpp"
#include "SokoState_v1.h"
using namespace std;
bool printAnswer(const SokoState& s)
{
s.printAnswer(cout);
cout << endl;
return true;
}
template <class T>
bool stopSearch(const T&)
{
return true;
}
int main(int argc, char *argv[])
{
SokoState initState;
cin >> initState;
cout << initState;
StateSpaceTreeSearch<SokoState, true, OrderCheckDup> sokoSearch;
int n = sokoSearch(initState, printAnswer);
if (n == 0)
{
cout << "No answer." << endl;
}
return 0;
}