搜索:高级枚举
有顺序有策略地枚举状态空间中的节点,寻找问题 的解状态空间
由所有状态构成的状态树
经典八数码问题:
有一个3*3的棋盘,其中有0-8共9个数字,0表示空 格,其他的数字可以和0交换位置。求由初始状态 到达目标状态的步数最少的解?
8 2 3
1 4 6
5 7 0
—>
1 2 3
4 5 6
7 8 0
解析:
1。状态空间
2.搜索方法:BFS DFS
BFS:优先扩展浅层节点,逐渐深入。用队列保存待扩展的节点,从队首队取出节点,扩 展出的新节点放入队尾,直到找到目标节点(问题 的解)
广度优先搜索代码框架:
BFS(){
初始化队列;
while(队列不空且未找到目标结点){
取队列首结点;
访问;
由该结点扩展新结点,并放入队尾;
必要时要记住每个节点的父结点;
}
}
DFS:优先深入遍历靠前的节点。可以用栈实现,在栈中保存从起始节点到当前节点 的路径上的所有节点。一般用递归实现
深度优先搜索代码框架:
DFS(){
初始化栈;
while(栈不空且未找到目标结点){
取栈顶结点;
访问;
由该结点扩展新结点,并放入栈顶;
}
}
状态搜索中的 关键问题: 判重 :
新扩展出的节点如果和以前扩展出的节点相同,则 这个新节点就不必再考虑,如何判重?
状态数目巨大,如何存储?
怎样才能较快的找到重复节点?
合理编码,减小存储代价。
算法书上给出的方法有编码解码、哈希技术、和使用STL中的集合set。可以用count()方法判重。
经过以上分析,可以写出代码:
#include <algorithm>
#include <iostream>
#include <map>
#include <set>
#include <string.h>
#include <string>
#include <vector>
using namespace std;
typedef int Statues[9];
const int MaxStatues = 1000000;
Statues MyQueue[MaxStatues], goal; //所有状态的队列和目标状态
int dist[MaxStatues];
const int dx[] = {-1, 1, 0, 0};
const int dy[] = {-1, 1, 0, 0};
set<int> visited;
//判断状态s是否被访问过
int Is_Not_Visited(int s) {
int v = 0;
for (int i = 0; i < 9; i++)
v = v * 10 + MyQueue[s][i];
if (visited.count(v))
return -1;
visited.insert(v);
return 0;
}
int BFS() {
int front = 1, rear = 2;
while (front != rear) {
Statues &s = MyQueue[front];
if (memcmp(goal, s, sizeof(s) == 0))
return front;
int zero_position;
for (int i = 0; i < 9; i++)
if (!s[i])
zero_position = i;
int x = zero_position / 3;
int y = zero_position % 3;
for (int j = 0; j < 4; j++) {
//扩展新状态
int newX = x + dx[j];
int newY = y + dy[j];
int newZ = newX * 3 + newY;
if (newX >= 0 && newX <= 3 && newY >= 0 && newY <= 3) {
Statues &new_Statue = s[rear];
//将新状态放进队尾
memcpy(&new_Statue, &s, sizeof(s));
new_Statue[newZ] = s[zero_position];
new_Statue[zero_position] = s[newZ];
//初始状态到最终状态的距离(变换步骤)加一
dist[rear] = dist[front] + 1;
//如果这个状态没有被访问过
if (Is_Not_Visited(rear))
rear++;
}
}
front++;
}
return 0;
}