从程序到算法——深搜

来源:互联网 发布:英文小说推荐 知乎 编辑:程序博客网 时间:2024/05/18 02:21

今年的NOIP2017普及组T3简直了,T1T2那么水,T3突然要算法了,去年好像还没这么BT……目测是搜索走遍天下。{然而暴力寸步难行……}那就说说搜索吧。
搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。(反正百度百科上是这么说的)在我看来,搜索就是对直接穷举的一个小优化,使穷举有了方向,正因为这一点,搜索算法在走迷宫之类的题目中特别好用。(今年T3也是走迷宫来着)
在搜索算法中,深搜以其简洁的代码而备受喜爱,深搜的基础是递归,所以代码短小精悍,效率么……
看一道题:在N*N的迷宫内,“#”为墙,“.”为路,共上下左右4个方向可以走。从左上角(1,1)位置处走到右下角(n,n)位置处,可以走通则输出YES,不可以走则输出NO。(n≤20)
先po代码(本命Pascal):

program ex11;      const dx:array[1..4]of -1..1=(0,1,0,-1);            dy:array[1..4]of -1..1=(1,0,-1,0);      var i,j,n:longint;          a:array[0..21,0..21]of char;      procedure dfs(x,y:longint);{x,y为当前步的坐标}      var tx,ty,i:longint;      begin        a[x,y]:='#';        if(x=n)and(y=n)then begin writeln('Yes');halt;end;        for i:=1 to 4 do        begin          tx:=x+dx[i];ty:=y+dy[i];          if a[tx,ty]='.'then dfs(tx,ty);          a[x,y]:='.'        end;      end;      begin        readln(n);        fillchar(a,sizeof(a),'#');        for i:=1 to n do        begin          for j:=1 to n do          read(a[i,j]);          readln;        end;        dfs(1,1);        writeln('NO');      end.        

什么数据读入,预处理什么的不说了,主要看过程dfs。
先将当前这一步走到的设置成“墙”,防止由这一步走出的下一步走了回头路

a[x,y]:='#';

如果已经到达终点,就输出YES,然后直接退出程序

if(x=n)and(y=n)then begin writeln('Yes');halt;end;

穷举四个方向(没有到终点才会执行)

for i:=1 to 4 do

计算出走第i个方向时下一步的坐标,记为(tx,ty)

tx:=x+dx[i];ty:=y+dy[i];

如果下一步是“路”,则走向下一步

if a[tx,ty]='.'then dfs(tx,ty);

如果事实证明这种走法无法到达终点,那么将当前的坐标从“墙”还原为“路”

a[x,y]:='.'

由于错误的走法最终一定会导致在某一步后没有下一步可走,所以还原a[x,y]在扩展下一步的代码后面加上即可。
而主程序中的dfs(1,1)则是从起点(1,1)开始搜索。
深搜原理:一条道走到黑,不撞南墙不回头(数字为访问顺序)

最后总结一下深搜框架:

procedure dfs(){带参数};begin{将当前的节点做标记,如:改变节点的值、入栈……}if{当前节点为目标节点}then {进行合适的处理,如输出等等,并视情况退出子程序}for i:=1 to{由当前节点可以扩展的下一级节点数}dobeginif{第i种可行}thenbegin{保存一些有用值}{亦可不保存,视具体情况而定}dfs(){带下一节点的参数}{还原已保存的值}{亦可不还原,视具体情况而定}end;{解除标记,如:还原节点的值、出栈……}{亦可不解除标记,视具体情况而定}end;end;
原创粉丝点击