zoukankan      html  css  js  c++  java
  • 广搜入门(简单易懂!)

    一、广搜原理

    这里需要用到------队列!
    题目:农夫要抓牛,农夫可以向前走一步,向后走一步,向前走x步(x为农夫坐标),求最短走几次可以走到牛的坐标。
    模板:
    1、为了方便,定义一个结构体,代表当前x(坐标),steps(走了多少步)
    2、创建一个结构体队列a,queue<point> a;
    2、将起点,即农夫坐标加入队列,此时队列只有一个元素,编号1。
    3、取出队列头元素,将其扩展。将所有队头元素走一步能到达的元素加入队列。在这里就是分别把x=x+1,steps=steps+1和x=x-1,steps=steps+1和x=2*x,steps=steps+1加入队列,分别标号为2,3,4。此时队列中包括队头有四个元素。
    4、用queue.pop()方法删除头元素,因为接下来我们要处理下一个元素啦!删除队头后,队列中只有2,3,4,则我们再取队头2,进行扩展。
    5、重复第三步。
    6、每次取队头元素判断队头的x是否为牛的坐标,如果是,目的达到,返回队头元素对应的steps即为结果!!!
    

    当然,在以上扩展过程中,用vis[]数组标记下个点有没有被走过。因为以第一次扩展就标记了该点,所以以后经过该点都不会继续扩展,即储存了达到该点的最小距离!!!

    二、那我们来道题目练习一下吧!!

    题目鸣人和佐助
    已知一张地图(以二维矩阵的形式表示)以及佐助和鸣人的位置。地图上的每个位置都可以走到,只不过有些位置上有大蛇丸的手下,需要先打败大蛇丸的手下才能到这些位置。鸣人有一定数量的查克拉,每一个单位的查克拉可以打败一个大蛇丸的手下。假设鸣人可以往上下左右四个方向移动,每移动一个距离需要花费1个单位时间,打败大蛇丸的手下不需要时间。如果鸣人查克拉消耗完了,则只可以走到没有大蛇丸手下的位置,不可以再移动到有大蛇丸手下的位置。佐助在此期间不移动,大蛇丸的手下也不移动。请问,鸣人要追上佐助最少需要花费多少时间?

    输入
    输入的第一行包含三个整数:M,N,T。代表M行N列的地图和鸣人初始的查克拉数量T。0 < M,N < 200,0 ≤ T < 10
    后面是M行N列的地图,其中@代表鸣人,+代表佐助。*代表通路,#代表大蛇丸的手下。
    输出
    输出包含一个整数R,代表鸣人追上佐助最少需要花费的时间。如果鸣人无法追上佐助,则输出-1。

    这道题目大家先自己做一遍再来看解析,解析在代码块的下面。

    #include <bits/stdc++.h>
    using namespace std;
    struct pos
    {
    	int x,y,cha,t;//坐标为x,y,查克拉为cha,时间为t。
    	pos(int xx, int yy, int kk, int tt) : x(xx), y(yy), cha(kk), t(tt) {}//快捷入队方式,不是必须操作
    };
    int vis[209][209],m,n,t,o[4][2]={{1,0},{-1,0},{0,1},{0,-1}};//vis为每个点拥有的最大查克拉
    char b[209][209];//储存迷宫
    bool judge(int q,int w)//判断扩展时是否越界
    {
    	if(q>=0&&q<=m-1&&w>=0&&w<=n-1)
    		return true;
    	else
    		return false;
    }
    queue<pos> a;//创建队列
    void dfs()
    {
    	while(!a.empty())
    	{
    		pos ans=a.front();
    		a.pop();
    		if(b[ans.x][ans.y]=='+')
    		{
    			cout<<ans.t<<endl;
    			return;
    		}
    		for(int i=0;i<4;i++)//开始扩展!!!
    		{
    				int xx=ans.x+o[i][0],yy=ans.y+o[i][1];
    				if(judge(xx,yy)&&ans.cha>vis[xx][yy])//只有当当前查克拉大于该点时才继续扩展
    				{
    					if(b[xx][yy]=='#'&&ans.cha>0)//为‘#'就消耗一个查克拉嘛
    					{
    						
    						a.push(pos(xx,yy,ans.cha-1,ans.t+1));
    						vis[xx][yy]=ans.cha;
    					}
    					if(b[xx][yy]!='#')
    					{
    						vis[xx][yy]=ans.cha;
    						a.push(pos(xx,yy,ans.cha,ans.t+1));
    					}
    				} 
    		}
    	}
    	cout<<-1;//找不到就返回-1
    	return;
    }
    int main()
    {
    	cin>>m>>n>>t;
    	for(int i=0;i<m;i++)
    		cin>>b[i];
    	for(int i=0;i<m;i++)
    	{
    		for(int j=0;j<n;j++)
    		{
    			vis[i][j]=-1;//初始时每个点都应该可以走,所以都初始化为-1
    			if(b[i][j]=='@')
    			{
    				a.push(pos(i,j,t,0));
    				vis[i][j]=t;//该点的最大查克拉已知,直接添加
    			}
    		}
    	}
    	dfs();
    	return 0;
    }
    

    注意

    不难发现,这道题目有个坑点,你不能用vis标记地图某个点走没走过!!!
    走过的路还可以重复走,因为限制你的不是一面墙,而是你的查克拉多少!!只有把查克拉最大的情况考虑到,才能说一定走不到。不然你可以试试下面这组数据:
    10 10 1
    

    @#********
    *******###
    ******##
    ******##
    ******##
    ******##
    ******##
    ******##
    ******##
    *******##+
    如果按照原来标记的话,(0,2)会被最快的一种走法,一直往右走走过,此时查克拉标记为0,那么永远也走不到终点。当另一种走法绕过起点旁的#时,此时步数远大于2,即使查克拉为1,但是已经被标记为走过,不会再扩展。详细过程可以看上面的代码。

  • 相关阅读:
    php错误抑制符
    php执行运算符
    php中一个经典的!==的用法
    php实现简单验证码的功能
    jquery是什么
    php连接符
    php与java语法的区别
    考雅思策略
    php魔术常量
    PHP中数据类型转换的三种方式
  • 原文地址:https://www.cnblogs.com/iss-ue/p/12679641.html
Copyright © 2011-2022 走看看