迷宫问题 DFS


DFS要点:
1.根据是否需要“找到可达路径立即结束”,可以给DFS函数设置返回值为int或void,这样的话可根据返回值判断return(当然也可以设置一个全局变量flag,效果相同,但感觉返回值更好);
2.DFS函数传给下一次递归的变量视情况而定,一般情况下是路径的长度,每次都+1
3.DFS函数一般可以分为3个部分:不满足情况直接return,满足条件记录并return,分别朝四个方向探索,并注意递归和回溯;
4.如果规模太大,比如100×100,DFS时间会扛不住,考虑记录,即DP思维(如下面的第二题)。

小鼠迷宫问题

Time Limit: 1500ms   Memory limit: 65536K  有疑问?点这里^_^

题目描述

小鼠a与小鼠b身处一个m×n的迷宫中,如图所示。每一个方格表示迷宫中的一个房间。这m×n个房间中有一些房间是封闭的,不允许任何人进入。在迷宫中任何位置均可沿上,下,左,右4个方向进入未封闭的房间。小鼠a位于迷宫的(p,q)方格中,它必须找出一条通向小鼠b所在的(r,s)方格的路。请帮助小鼠a找出所有通向小鼠b的最短道路。 



 请编程对于给定的小鼠的迷宫,计算小鼠a通向小鼠b的所有最短道路。

输入

本题有多组输入数据,你必须处理到EOF为止。
每组数据的第一行有3个正整数n,m,k,分别表示迷宫的行数,列数和封闭的房间数。
接下来的k行中,每行2个正整数,表示被封闭的房间所在的行号和列号。
最后的2行,每行也有2个正整数,分别表示小鼠a所处的方格(p,q)和小鼠b所处的方格(r,s)。

输出

对于每组数据,将计算出的小鼠a通向小鼠b的最短路长度和有多少条不同的最短路输出。
每组数据输出两行,第一行是最短路长度;第2行是不同的最短路数。
每组输出之间没有空行。
如果小鼠a无法通向小鼠b则输出“No Solution!”。

示例输入

8 8 3
3 3
4 5
6 6
2 1
7 7

示例输出

11
96

来源

NOI’2005福建省选手选拔赛


#include<iostream>  
#include<algorithm>  
#include<stdio.h>  
#include<string.h>  
#include<stdlib.h>  
  
using namespace std;  
  
struct node  
{  
    int x;  
    int y;  
}a,b;  
  
int move_x[] = {0,1,0,-1};  
int move_y[] = {1,0,-1,0};  
int v[101][101];  			//标记是否到达过
int map[101][101];  
int n,m,k;  
int flag;  
int ans;  	//已知最短路的长度
int psum;  //当前已知的最短路找到的次数
  
void DFS(int xx,int yy,int cnt)  //cnt : 当前距离
{  
    if(cnt>ans)  	//结束条件。已然大于曾找过的最短路了,再找下去也没意义了
    {  
        return ;  
    }  
    if(xx == b.x && yy == b.y)  
    {  
        if(cnt<ans)  		//如果此路更短,则之前的较长的答案可以抛弃了
        {  
            flag = 1;  		//标记,找到了可达的路
            ans = cnt;  
            psum = 1;  
        }  
        else if(cnt == ans)  //如果和之前的路一样长,记下来
        {  
            psum++;  
        }  
        return ;  
    }  
    for(int i=0;i<4;i++)  //朝四个方向探索
    {  
		int tmpX = xx + move_x[i];
		int tmpY = yy + move_y[i];
        if(tmpX >= 0 && tmpX < n && tmpY >= 0 && tmpY < m 		//判断越界
        	&& map[tmpX][tmpY] == 0 && v[tmpX][tmpY] == 0)  	//判断可达到且没到达过
        {  
            v[tmpX][tmpY] = 1;  
            DFS(tmpX, tmpY, cnt + 1);  	//DFS,路径长度+1
            v[tmpX][tmpY] = 0;  		//回溯
        }  
    }  
}  
  
int main()  
{  
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)  
    {  
        for(int i=0;i<=n;i++)  
        {  
            for(int j=0;j<=m;j++)  
            {  
                map[i][j] = 0;  
                v[i][j] = 0;  
            }  
        }  
        int x,y;  
        for(int i=0;i<k;i++)  
        {  
            scanf("%d%d",&x,&y);  
            map[x][y] = 1;  
        }  
        ans = 99999999;  
        flag = 0;  
        psum = 0;  
        scanf("%d%d",&a.x,&a.y);  
        scanf("%d%d",&b.x,&b.y);  
        DFS(a.x,a.y,0);  
        (flag == 0) ? printf("No Solution!\n") : printf("%d\n%d\n", ans, psum); 
    }  
    return 0;  



逃离迷宫

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 12273    Accepted Submission(s): 2964


Problem Description
  给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,gloria想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,gloria可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,gloria不能走到迷宫外面去。令人头痛的是,gloria是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,gloria所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。gloria能从一个位置走到另外一个位置吗?
 

Input
  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,
  第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符'.'表示该位置为空地,字符'*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x 1, y 1, x 2, y 2 (1 ≤ k ≤ 10, 1 ≤ x 1, x 2 ≤ n, 1 ≤ y 1, y 2 ≤ m),其中k表示gloria最多能转的弯数,(x 1, y 1), (x 2, y 2)表示两个位置,其中x 1,x 2对应列,y 1, y 2对应行。
 

Output
  每组测试数据对应为一行,若gloria能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。
 

Sample Input
  
  
2 5 5 ...** *.**. ..... ..... *.... 1 1 1 1 3 5 5 ...** *.**. ..... ..... *.... 2 1 1 1 3
 

Sample Output
  
  
no yes

#include<iostream>
#include<cstdio>
#include<cstring>

#define N 101
#define INITIAL_DIR -3
#define FIND 1
#define NOT 0

using namespace std;

char map[N][N];
int turnTimes[N][N];		//记录到达每一个点时最小的转向次数,可以根据这个判断当前点是否已经走过,也可以进行剪枝
int times, m, n, k, xx1, yy1, xx2, yy2;        
int a[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};

void init_turnTimes()
{
	for(int i = 0; i < N; i++)
	{
		for(int j = 0; j < N; j++)
		{
			turnTimes[i][j] = 0x3fffffff;
		}
	}
}

int DFS(int x, int y, int dir)		//带返回值,可以方便的根据返回值得到“是否已经找到答案”这一信息
{
	if(turnTimes[x][y] > k || (x != xx2 && y != yy2 && turnTimes[x][y] == k))	//结束条件,其实第二个可以不要,效果并不明显
	{
		return NOT;
	}
	if(x == xx2 && y == yy2)	//结束条件,发现了终点
	{
		return FIND;
	}
	for(int i = 0; i < 4; i++)	//分别向四个方向探索
	{
		int tmpX = x + a[i][0];
		int tmpY = y + a[i][1];
		if(tmpX >= 0 && tmpX < m && tmpY >= 0 && tmpY < n			//越界条件
			&& map[tmpX][tmpY] != '*' && turnTimes[tmpX][tmpY] >= turnTimes[x][y])	//要探索的点可达且没去过
		{
			//剪枝条件。相当于DP的记录,因为100×100直接DFS太丧心病狂
			//其实可以在if判断语句里用turnTimes[tmpX][tmpY] > turnTimes[x][y]直接剪掉,但是为了突出剪枝条件,刻意保留,在此处展示
			//如果需要转弯+1,且之前在此点转向次数和过来的点已然相同,就不用再探索了,因为如果之前都不符合条件,+1之后更不可能符合条件了
		    if(dir != INITIAL_DIR && dir !=i && turnTimes[tmpX][tmpY] == turnTimes[x][y])     
                continue;

            if(dir != INITIAL_DIR && dir != i)
            {
                turnTimes[tmpX][tmpY] = turnTimes[x][y] + 1;
            }
			else
            {
                turnTimes[tmpX][tmpY] = turnTimes[x][y];
            }
			if(DFS(tmpX, tmpY, i) == FIND)		//回溯,且已经找到立即return,不必再继续向后探索。
			{
				return FIND;
			}

		}
	}
	return NOT;
}

int main()
{
    cin >> times;
    while(times--)
    {
    	memset(map, 0, sizeof(map));
    	init_turnTimes();
    	cin >> m >> n;
	   	for(int i = 0; i < m; i++)
	    {
	    	cin >> map[i];
	    }
	    cin >> k >> yy1 >> xx1 >> yy2 >> xx2;	//建议以后设成sx, sy; ex, ey; 因为y1已经被math.h占用了,丧心病狂
	    xx1--, yy1--, xx2--, yy2--;

	    turnTimes[xx1][yy1] = 0;		//将起始点的转向次数初始化为0

        (DFS(xx1, yy1, INITIAL_DIR) == FIND) ? cout << "yes\n" : cout << "no\n";
    }
    return 0;
}

在这里顺便奉上这一题的BFS的源码。值得注意的是,和普通的BFS以距离为关键点比起来,这一题的BFS有点儿变化,已经在代码中标出:

#include<iostream>  
#include<cstring>  
#include<cstdio>   
#include<string>  
#include<queue>  
using namespace std;  
int enx,eny,m,n;  
char maze[105][105];
int totalStep;  		//totalStep: total directions
int visi[105][105];  	//visi : visited
int turnTimes[105][105];  		//turnTimes: turn around times of every vertex
  
int dir[4][2]={{-1,0},{1,0},{0,1},{0,-1}};  
struct node  
{  
    int x;  
    int y;  
};  
  
void BFS(node p)  
{  
    queue<node> mq;  
    mq.push(p);  									// push the start point in the queue
    node curPoint, nextPoint;  
  
    while(!mq.empty())  
    {  
        curPoint = mq.front();  
        mq.pop();  
        if(turnTimes[curPoint.x][curPoint.y] >= totalStep) break;  
  
        int tmpX, tmpY;  
        for(int i = 0; i < 4; i++)  
        {  
            tmpX = curPoint.x + dir[i][0], tmpY = curPoint.y + dir[i][1];  
            while(tmpX >= 0 && tmpX < m && tmpY >= 0&& tmpY< n			//越界判断
					&& maze[tmpX][tmpY] != '*')  							//可达
            {  
                if(!visi[tmpX][tmpY])  
                {  
                    visi[tmpX][tmpY] = 1;  				// 将探索到的可达的点nextPoint标记
                    nextPoint.x = tmpX, nextPoint.y = tmpY;  
                    turnTimes[tmpX][tmpY] = turnTimes[curPoint.x][curPoint.y] + 1;  	// +1
                    mq.push(nextPoint);  				// 将nextPoint入队
                }  
                tmpX += dir[i][0], tmpY += dir[i][1];  	//朝一个方向走到底!
				//由于本题的关键是转向的次数,所以和普通的BFS比起来(普通BFS关键是“距离”),关键不再是距离,于是只要方向没变,即使距离不一样,那么这些格也是等价的,所以“朝一个方向走到底”是这个题的“转向次数是关键”的解决办法。
            }  
        }  
    }  
}  
  
int main()  
{  
     int times, stx, sty;  
     cin >> times;  
     while(times--)  
     {  
         cin>>m>>n;  
         for(int i = 0; i < m; i++)  
            cin >> maze[i];  
         cin >> totalStep;  
         cin >> stx >> sty >> enx >> eny;  
         stx--,sty--,enx--,eny--;  
         swap(stx,sty); swap(enx,eny);  
         node p;  
         p.x = stx,p.y = sty;  
         memset(visi,0,sizeof(visi));  
         for(int i = 0; i < m; i++)  
            for(int j = 0; j < n; j++)  
                turnTimes[i][j] = 1e8;  
         visi[p.x][p.y] = 1;  
         turnTimes[p.x][p.y] = -1;  

         BFS(p);  
  
         (turnTimes[enx][eny] <= totalStep) ? cout << "yes" << endl : cout << "no" << endl;
     }  
     return 0;  
}  



  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值