java迷宫问题--广度优先算法

题目描述

小明置身于一个迷宫,请你帮小明找出从起点到终点的最短路程。
小明只能向上下左右四个方向移动。

输入

输入包含多组测试数据。输入的第一行是一个整数T,表示有T组测试数据。
每组输入的第一行是两个整数N和M(1<=N,M<=100)。
接下来N行,每行输入M个字符,每个字符表示迷宫中的一个小方格。
字符的含义如下:
‘S’:起点
‘E’:终点
‘-’:空地,可以通过
‘#’:障碍,无法通过
输入数据保证有且仅有一个起点和终点。

输出

对于每组输入,输出从起点到终点的最短路程,如果不存在从起点到终点的路,则输出-1。

样例输入

1
5 5
S-###
-----
##---
E#---
---##

样例输出

9

思路:

 迷宫问题利用广度优先算法,设置备选项,深搜会超时

广度优先算法有模板:

大体是:

入队,

记录,计算

队不为空时循环while(!queue.isEmpty()){

队首出队,操作得到新元素,判断,记录计算,新元素入队

出队

}

 

代码:



import java.util.ArrayList;
import java.util.Scanner;

public class Main {
	
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		
		int t = in.nextInt();
		for(int k=0;k<t;k++) {
		int n = in.nextInt();
		int m = in.nextInt();
		char map[][] = new char[n+1][m+1];//迷宫
		boolean vist[][] = new boolean[n+1][m+1];//记录是否被访问,默认值为false
		int l[][] = new int[n+1][m+1];//记录路径
		Point s = new Point(0,0);
		Point e = new Point(0,0);
		
		//初始化迷宫并且找到出口入口坐标
		for (int i = 1; i < n+1; i++) {
			String str = in.next();
			for (int j = 1; j < m+1; j++) {
				map[i][j] = str.charAt(j-1);//java输入char类型解决
				//找到 s e;
				if(map[i][j]=='S') {
					s.setXY(i, j);
					
				}
				if(map[i][j]=='E') {
					e.setXY(i, j);
					
				}
			}
			
		}
		int length = getShortPath(map,s,e,vist,l);
		System.out.println(length);
		}
		in.close();
		
   
    
	}

	private static int getShortPath(char[][] map, Point s, Point e, boolean[][] vist,int[][] l) {
		//方向
		int dir[][]= {
				{-1,0},//左
				{1,0},//有
				{0,-1},//下
				{0,1}//上
				
		};
		
		int ans=-1;
		
		ArrayList<Point> queue = new ArrayList<Point>();
		
		queue.add(s);//入口入队,当队不为空是进行循环
		vist[s.x][s.y]=true; //记录入口已访问
		l[s.x][s.y]=0; //路径以入口为起点
		
		s:while(!queue.isEmpty()) {
			//四个方向开始探索
			Point first = queue.get(0);//得到队首元素;
			//进行上下左右移动试探
			for (int i = 0; i < dir.length; i++) {
				Point temp = new Point(first.x+dir[i][0],first.y+dir[i][1]);
				//判断走得通,且不越界
				if(temp.x>0&&temp.x<map.length&&
				   temp.y>0&&temp.y<map[0].length&&
				   map[temp.x][temp.y]!='#'&&
				   vist[temp.x][temp.y]==false) {
					//判断当前是否是出口
					if(map[temp.x][temp.y]=='E') {
						ans=l[first.x][first.y]+1;
						break s;//s标记跳出循环的位置
					}
						//走得通但是不是出口,记录已经访问,路程递增,入队
						vist[temp.x][temp.y]=true; 
						l[temp.x][temp.y] =l[first.x][first.y]+1;
						queue.add(temp);
				
					
				}
				
			}
			queue.remove(0);
		}
		return ans;
	}
}
 class Point{
	int x,y;

	public Point(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	public void setXY(int x,int y) {
		this.x = x;
		this.y = y;
	}
	
};

思考:

1.二维数组存内容,下标与点类坐标一一对应该,创建点类是为了方便操作

2.设置备选项,是为了不免重复,因为这个过程是不断试错并及时改正的过程(回溯)

3.语法:java中的队列用的是ArrayList queue,入队即添加元素 queue.add(s) 获得队首元素 queue.get(0),队首元素出队queue.remove(0)

4.在java中输入char类型变量没有.in.nextChar()方法

解决:1.char a=in.next.charAt(0) 利用的是取string的第一个元素,输入单个变量的时候可以,但是在初始化数组的时候行不太通,必须要用空格分开,不然会出错

           2.处理上面说得到问题解决数组的输入,用到的就是代码贴出来的部分了,同样也是用的string,但不是在输入的时候取。而是在输入之后利用循环一一填入数组中

5.break的用法:当有多个循环的时候,如何确定是跳出的那个循环,可以利用标记

      例如 s: while(**){ ***;  break s;} 

再附上C++算法

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;

char Map[100][100];            /*    存储图    */
bool vis[100][100];            /* 标记走过的点 */
bool finds = false;            /*   是否找到   */
int  map_high, map_width;      /*    图边界    */
int  moveX[4] = { -1,0,1,0 };  /*   四个方向   */
int  moveY[4] = { 0,-1,0,1 };

struct status {                /* 存储当前状态 */
    int X, Y, step;
};

bool check(status next) {      /*   剪枝条件   */
    if (next.X >= 0 && next.X < map_high)
        if (next.Y >= 0 && next.Y < map_width)
            if (Map[next.X][next.Y] != '#' && vis[next.X][next.Y] != true)
                return true;
    return false;
}

void BFS(status start) {
    queue<status> Search;                   /*     定义队列     */
    Search.push(start);                     /*     起点入列     */
    while (!Search.empty()) {               /*  不为空就一直搜  */
        status now = Search.front();        /*    访问队首元素  */
        if (Map[now.X][now.Y] == 'E') {     /*       出口       */
            finds = true;
            cout << now.step << endl;
            return;
        }
        status next;                        /*   四个方向拓展   */
        for (int i = 0; i < 4; i++) {
            next.X = now.X + moveX[i];
            next.Y = now.Y + moveY[i];
            next.step = now.step + 1;

            if (check(next) == true) {      /*      合法点      */
                vis[next.X][next.Y] = true; /*    标记,入列    */
                Search.push(next);
            }
        }
        Search.pop();                       /*   队首元素出列   */
    }
    if (finds == false)
        cout << -1 << endl;
}

int main() {
    int num; cin >> num;
    while (num--) {
        /*             初始化图              */
        memset(Map, 0, sizeof(Map)); memset(vis, false, sizeof(vis));
        cin >> map_high >> map_width; cin.get();
        for (int i = 0; i < map_high; i++) {
            gets(Map[i]);
        }

        /*              寻找起点            */
        bool first = false;
        int positiom1, positiom2;
        for (positiom1 = 0; positiom1 < map_high; positiom1++) {
            for (positiom2 = 0; positiom2 < map_width; positiom2++)
                if (Map[positiom1][positiom2] == 'S') {
                    first = true; break;
                }
            if (first == true) break;
        }

        /*            初始化起点            */
        status start;
        start.X = positiom1;
        start.Y = positiom2;
        start.step = 0;
        vis[start.X][start.Y] = true;
        finds = false;

        BFS(start);
    }
    return 0;
}

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值