搜索,就是列举所有情况,一种一种的列,但是在当前情况下又会分成好几个状态,还要继续列举,状态,每一个状态都会有一个可能解,从一个状态转移到另一个状态,进行搜索的进一步延伸,
一、BFS和队列
一般用队列来实现广搜
找出所有的大情况,再把每种大情况的下一阶段找出,一层一层的寻找,广搜离不开队列,可以求最短路径
算是一个模板吧
struct node
{
int xx,yy,step;
};
void bfs(int xx,int yy)
{
queue<node>q;
memset(v,0,sizeof(v));
node s,ss;
s.xx=xx;
s.yy=yy;
s.step=0;
q.push(s);//赋初始值
while(!q.empty())
{
s=q.front();//取队首元素
q.pop();//删除队首元素
for(int i=0;i<12;i++)//把移动方式全部执行一遍
{
r=s.xx+x[i];
l=s.yy+y[i];
if(r<=100&&r>0&&l<=100&&l>0&&v[r][l]==0)//符合范围条件且未走过
{
v[r][l]=1;//标记走过
ss.xx=r;
ss.yy=l;
ss.step=s.step+1;
q.push(ss);//放入移动后的信息
if(r==1&&l==1)
{
cout<<s.step+1<<endl;
return ;
}
}
}
}
}
二、八数码问题和状态图搜索
BFS搜索处理的对象换成“状态”,即状态图搜索问题
1、八数码问题是典型的状态图搜索问题
在3×3的棋盘上,摆上标有1至8的某一数字的八个棋子。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局和目标布局,计算出最少移动步数和数码的移动数列。
我们回想一下普通的广搜,就是把所有情况都列举一遍,里面必定会有重复的状态,但这一棋局共有9!种状态,一遍一遍的查重又非常繁琐,所以用“康托展开”来判重(利用哈希函数)。
2、康托展开
康托展开的功能是:计算出给出排列数的相应位置
例如,总共有9!个数,第一个数的位置是0,以此类推最后一个数的位置就是9!-1,康托函数就是把排列,转化成相对应的位置
然后用这个函数判断每一个新状态是否已经处理过了(详细的原理就不说了)
直接写用法
int factorial[20]={1,1,2,6,24,120,720,5040,40320,362880,3628800}; //cantor需要用到的常数,固定的
int flag[20];//标记数组
bool cantor(int num[],int n)//康托函数
{
int x=0;
for(int i=0;i<n;i++){//遍历num数组
int cot=0;
for(int j=i+1;j<n;j++)//找到小于num[i]的数字
{
if(num[i]>num[j]) cot++;//计算未出现的元素排在第几个
}
x+=cot*factorial[n-i-1];
}
if(!flag[x])
{
flag[x]=1;
return 1;
}
else return 0;
}
补充:给出全排列后,给出一个数字k,返回第k大的排列,就是逆康托展开(暂不使用,先放着吧)
3、A* 算法
启发式搜索,A*算法=“BFS+贪心”
曼哈顿距离就是起点和终点的横纵坐标距离之和
三、双向广搜(DBFS)
双向广搜从起点(正向搜索)和终点(逆向搜索)同时开始搜索,直到在某个位置遇到。前提是直到起点和终点
哦,没有代码,有空粘
总结:之前在学校没好好学搜索,也没做过题,现在重新学习,发现书上很多新名词和新型的算法,因为做题经验太少,不知道是不是所有的都很有必要去学习,如果一直往外拓展延伸会花费太多时间,毕竟时间还是很紧张的,所以先把一些莫名其妙的太陌生的方法放弃,如果做题的时候用到再重新接触和学习。