Modelling successive states,我们常用的方法是directed graph。
Exercise
A farmer, a wolf, a goat, and a cabbage are on one side of a river that they must cross.
There is a boat that can be operated only by the farmer, and with only two places. When
the farmer is not there, the wolf will eat the goat, and the goat will eat the cabbage.
BFS可用来解决计算机游戏(例如即时策略游戏)中找寻路径的问题。在这个应用中,使用平面网格来代替图形,而一个格子即是图中的一个节点。所有节点都与它的邻居(上、下、左、右、左上、右上、左下、右下)相接。
值得一提的是,当这样使用BFS算法时,首先要先检验上、下、左、右的邻居节点,再检验左上、右上、左下、右下的邻居节点。这是因为BFS趋向于先查找斜向邻居节点,而不是四方的邻居节点,因此找到的路径将不正确。BFS应该先查找四方邻居节点,接着才查找斜向邻居节点。 ——维基百科
我们搜索路径的环境应用以下代码,代码作者Didier Lime的主页可以浏览
// Example of world building, display, and successor computation for the artificial
// intelligence path-finding lab
//
// Author: Didier Lime
// Date: 2018-10-03
#include <iostream>
#include <list>
#include <cstdlib>
#include <ctime>
using namespace std;
class World
{
private:
// Number of columns
unsigned int L;
// Number of lines
unsigned int H;
// Unidimensional array for tiles
int* w;
public:
// Constructor
World(unsigned int L, unsigned int H, double P)
{
this->L = L;
this->H = H;
this->w = new int[L*H];
// Add walls to the first and last columns
for (unsigned int i = 0; i < H; i++)
{
this->w[i * L] = 1;
this->w[i * L + L - 1] = 1;
}
// Add walls to the first and last lines
for (unsigned int j = 0; j < L; j++)
{
this->w[j] = 1;
this->w[(H - 1) * L + j] = 1;
}
for (unsigned int i = 0; i < H; i++)
{
for (unsigned int j = 0; j < L; j++)
{
// add a wall in this tile with probability P and provided that it is neither
// the starting tile nor the goal tile
if ((double) rand() / RAND_MAX < P && !(i == 1 && j == 1) && !(i == H - 2 && j == L - 2))
{
this->w[i * L + j] = 1;
}
}
}
}
// Display the world
void display()
{
for (unsigned int i = 0; i < H; i++)
{
for (unsigned int j = 0; j < L; j++)
{
switch (this->w[i * L + j])
{
case 0:
cout << ".";
break;
case 1:
cout << "W";
break;
}
}
cout << endl;
}
}
// compute the successors of tile number i in world w
// we return the number n of valid successors
// the actual list is in array r where only the first n
// elements are significant
unsigned int successors(unsigned int i, unsigned int r[4])
{
unsigned int n = 0;
if (i >= 0 && i < this->L * this->H && this->w[i] != 1)
{
// if i is a correct tile number (inside the array and not on a wall)
// look in the four adjacent tiles and keep only those with no wall
const unsigned int moves[] = { i - 1, i + 1, i - L, i + L};
for (unsigned int k = 0; k < 4; k++)
{
if (this->w[moves[k]] != 1)
{
r[n] = moves[k];
n++;
}
}
}
return n;
}
// Depth-first search
// starting from tile number s0, find a path to tile number t
// return true if such a path exists, false otherwise
// if it exists the path is given in variable path (hence the reference &)
bool dfs(unsigned int s0, unsigned int t, list<unsigned int>& path)
{
bool r = false;
// ... Complete here ...
return r;
}
};
int main()
{
// Initialise the random number generator
srand(time(0));
// Create a world
World w(20, 10, 0.2);
// Display it
w.display();
// Print the tile numbers of the successors of the starting tile (1, 1)
unsigned int succs[4];
unsigned int n = w.successors(21, succs);
for (unsigned int k = 0; k < n; k++)
{
cout << succs[k] << " ";
}
cout << endl;
return 0;
}
无论用到什么算法,我们都需要设置开始位置和目标位置。也需要有记录当前位置和离开出发点的距离。
struct position
{
int x;
int y;
};
//typedef struct point pt;可以改写名字
typedef pair<point,int> p;//用pair存储位置和距离
//这种情况是我们在建立地图时候就随机生成了起点和终点。
position start_point,end_point;
for(int i=1;i<=n;i++){ //遍历行
for(int j=1;j<=m;j++){ //遍历列
cin>>mp[i][j]; //输入
if(mp[i][j]=='s'){ //如果是起点
start_point.x=i; //记下X
start_point.y=j; //记下Y
}
if(mp[i][j]=='g'){//如果是终点
end_point.x=i; //记下X
end_point.y=j; //记下Y
}
}
}
//如果我们已经知道了两点的具体位置
//A robot tries to move from coordinates (1, 1) (top-left of the screen)
//to coordinates (L−2, H −2) (bottom-right of the screen)
position start_point={{1,1}};
position end_point={{L-2,H-2}};
广度优先搜索Breadth First Search
为了更好的理解BFS搜索算法,我们来看一看相关代码。
std::queue<node *> visited, unvisited;
node nodes[9];
node *current;
unvisited.push(&nodes[0]); // 先把root放入unvisited queue
while (!unvisited.empty()) { // 只有unvisited不空
current = (unvisited.front()); // 目前應該檢驗的
if (current->left != NULL)
unvisited.push(current->left); // 把左邊放入queue中
if (current->right != NULL) // 右邊壓入。因為QUEUE是一個先進先出的結構构,所以即使後面再壓其他东西,依然會先訪問這個。
unvisited.push(current->right);
visited.push(current);
cout << current->self << endl;
unvisited.pop();
}
直接运行是会出错的。天下就是没有免费的午餐,或早或晚,一定是要付出代价的。
bfs.cpp:1:6: error: ‘queue’ in namespace ‘std’ does not name a template type
std::queue<node *> visited, unvisited;
^~~~~
bfs.cpp:3:1: error: ‘node’ does not name a type
node nodes[9];
^~~~
bfs.cpp:5:1: error: ‘node’ does not name a type
node *current;
^~~~
bfs.cpp:8:1: error: ‘unvisited’ does not name a type; did you mean ‘unsigned’?
unvisited.push(&nodes[0]); // 先把root放入unvisited queue
^~~~~~~~~
unsigned
bfs.cpp:11:1: error: expected unqualified-id before ‘while’
while (!unvisited.empty()) { // 只有unvisited不空
^~~~~
首先是没有包含全头文件,要添加相关#include<>文件。
其次是没有node这个数据类型,自己要添加struct或者class。
bfs.cpp: In function ‘int main()’:
bfs.cpp:24:18: error: ‘struct main()::node’ has no member named ‘left’
if (current->left != NULL) ^~~~
然后,我们建立的node里面要有成员left,right,self。不然依然在报错。
累了,休息一会儿,阅读别人的代码看看。