搜索算法是利用计算机的高性能来有目的地穷举一个问题的部分或所有的可能情况,从而求出问题的解的一种方法。
相比于单纯的枚举算法有了一定的方向性和目标性。算法是在解的空间里,从一个状态转移(按照要求拓展)到其他状态,这样进行下去,将解的空间中的状态遍历,找到答案(目标的状态)。
在深入研究搜索算法之前,需要重新认识一下状态和状态转移
状态(state)是对问题在某一时刻进展情况的数学描述,或者是数学抽象。
每一个状态都会是答案的一个“可能的”解。状态的转移就是问题从一个状态转移到另一个状态,这样就可以进行搜索的一步步延伸,最后要得到的解也是其中的一个状态。
这里的状态和状态转移和线性dp中的状态和状态转移是一个概念,只是实际运用的过程中使用的对象不一样,比如线性dp中运用状态转移方程是为了获得一个更优的解,是为了减少时间复杂度和节约计算成本。
而搜索中的
1.深度优先搜索(DFS)
从初始位置开始,用给定的规则按层开始搜索结点,找到合适的结点并与描述的目标结点进行对比,检测是否相同。若不相同则生成下一层任意节点并反复该过程,到叶节点结束(即不能再生成新状态结点),取另一可能扩展搜索状态时,回溯到上一层结果,取另一可能扩展搜索的分支。采用相同办法一直进行下去,直到找到目标状态为止。
void dfs(int step){
判断边界
尝试每一种可能 for(i=1;i<=n;i++){
继续下一步 dfs(step+1)
}
返回
}
以上是dfs的一种基本构思模型,我发现很多情况下看写出来的程序比看单纯的文字更舒服,看文字不看懂的地方看程序一会就明白了(这难道是程序员的通病吗)
结合图片理解,dfs就是按照层进行搜索,一层一层的走,以深度为优先级,要尽快的下到下一层中,一旦无法达到下一层,就进行回溯,返回最近的上一层当中去:
1.从起始位置开始
2.尽快的搜索到下一个层中去
3.以深度为优先,不考虑同一层中的结点,也就是说,dfs以下一个结点为目标,如果下一个结点不存在,dfs算法会进行回溯,回到上一层中,再以下一个层次中的结点为目标
4.同一个子分支中的所有叶节点被搜索完毕后,才会进行到上一个层次中的下一个结点。
递归实现:
Function Dfs (Int Step, 当前状态)
Begin
可加结束条件
从当前状态循环拓展下一个状态Next
If 状态Next合法 Then
Dfs (Step + 1, Next )
End
dfs需要用到递归算法的知识,递归的条件是可以作为深度优先算法的边界条件的
【例】设有n个整数的集合{1,2,…,n},从中取出任意r个数进行排列(r<n),试列出所有的排列。//ppt上的例题
这和全排列的题目类似,但是这个题目限制了排列对象
#include<cstdio>
#include<iostream>
#include<iomanip>
using namespace std;
int num=0,a[10001]={0},n,r;
bool b[10001]={0};
int search(int); //回溯过程 回溯的过程就是返回上一个结点对下一个层次中的其他叶节点搜索做准备
int print(); //输出方案
int main()
{
cout<<"input n,r:";
cin>>n>>r;
search(1);
cout<<"number="<<num<<endl; //输出方案总数
int search(int k)
{
int i;
for (i=1;i<=n;i++)
if (!b[i]) //判断i是否可用
{
a[k]=i; //保存结果
b[i]=1;
if (k==r) print();
else search(k+1);
b[i]=0;
}
}
int print()
{
num++;
for (int i=1;i<=r;i++)
cout<<setw(3)<<a[i];
cout<<endl;
}
一个整数N,找到1-N的全排列。如输出1-3的全排列:(1,2,3) (1,3,2) (2,1,3) (2,3,1) (3,1,2) (3,2,1)(全排列)
从网上搜索到的全排列例题和解决方案,发现和上面的题目的思路基本一致
#include<iostream>
using namespace std;
int n = 3;
int seat[3] = { 0 };//三个位置,所放的数字先全部用0初始化
int mark[3] = { 0 };//分别标记0,1,2三个数组是否放置在了位置上,如已经放置值用1表示,没放置用0。
void dfs(int seat_id)
{
if (seat_id == n)//位置是从0开始的,如果将卡片放完一遍,则退出
{
for (int i = 0; i < n; i++)//打印每个位置上所放置的卡片号
{
cout << seat[i]<< " ";
}
cout << endl;
return; //需要回退到调用处
}
for (int card_id = 0; card_id < n; card_id++)//将所有可能的卡片尝试放到当前位置上
{
if (mark[card_id] == 0)//如果卡card_id还没有被使用
{
seat[seat_id] = card_id;//将卡片放到位置上
mark[card_id] = 1;//标记为已使用
dfs(seat_id + 1);//进入下一步,在下一个位置再次执行
mark[card_id] = 0;//将尝试过(放到位置上)的卡片收回
}
}
return;//结束函数,返回到调用处
}
int main(int argc, char* argv[])
{
dfs_permutations(0);
return 0;
}