HDU 1728 解题报告 ---- 逃离迷宫

HDU 1728 逃离迷宫 http://acm.hdu.edu.cn/showproblem.php?pid=1728 

对于代码32行,为什么等于不能随便剪掉 
如果剪掉就会出现下图结果: 
【假如转弯数k=1,起点终点如图】 
那么如果你的代码是优先向右搜索就会出错 
红色的线是先搜的,由于最多转一次弯,所以不合题意; 
蓝色是后搜的,因为遇到转弯数相等所以不往下走了了,但是继续走是可以满足题意输出"yes"的 



深搜:


#include <iostream>
using namespace std;
#define  inf 0x3fffffff//无穷大
#define  M 105//数组大小
//1、wan用于转弯数剪枝;2、step用于步数兼职,就不用visit标记访问了
int r,c,ex,ey,k,wan[M][M];//行数,列数,终点x,终点y,弯数
char map[M][M];//接收控制台字符串
int x_move[4] = {-1, 0, 1, 0};//四方向移动  
int y_move[4] = {0, 1, 0, -1};//四方向移动  
bool flag;  //判断成功

void dfs(int x,int y,int dir)//dir 为当前方向
{
    if(x==ex&&y==ey)
    {
        if(wan[x][y]<=k)
            flag=true;
        return ;
    }
    if(wan[x][y]>k)//转弯数超过k不再往下走
        return;
    //x!=ex && y!=ey 说民必须至少转一次弯,但是已经不能再转了
    if(wan[x][y]==k&&x!=ex&&y!=ey)
        return ;
    for (int i=0;i<4;i++)
    {
        int tx=x+x_move[i];
        int ty=y+y_move[i];
        if(tx<0||tx>=r||ty<0||ty>=c)//若出界
            continue;
        //转弯数相等不再剪枝,所以wan[tx][ty]<wan[x][y]而不是<=
        if(map[tx][ty]=='*'||wan[tx][ty]<wan[x][y])//剪枝拐弯数:下一步转弯数小于前一步拐弯数,说明已遍历且更优,无须再遍历
            continue;
        if(dir!=-1&&i!=dir&&wan[tx][ty]<wan[x][y]+1)//剪枝拐弯数:此拐弯举动,若下一步原有拐弯数比当前步进入该处拐弯数小,说明已遍历且更优,无须再遍历
            continue;
        if(dir!=-1&&i!=dir)
            wan[tx][ty]=wan[x][y]+1;//拐弯了
        else 
            wan[tx][ty]=wan[x][y];//没拐弯,拐弯数不变
        dfs(tx,ty,i);
        if(flag)
            return ;
    }
}

int main()
{
    int t,i,j,sx,sy;//sx,xy是起点
    scanf("%d",&t);

    while (t--)
    {
        scanf("%d%d",&r,&c);
        for(i=0;i<r;i++)
            scanf("%s",map[i]);
        scanf("%d%d%d%d%d",&k,&sy,&sx,&ey,&ex);//到此,结束一趟控制台数据
        sx--,sy--,ex--,ey--;//我从0开始编号,而题目是从1开始
        for (i=0;i<r;i++)
            for (j=0;j<c;j++)
                wan[i][j]=inf;//初始化转弯数和步数无穷大
        wan[sx][sy]=0;//到达起点转弯数0
        flag=false;
        dfs(sx,sy,-1);//dir=-1表示:一开始走任意方向
        if(flag)
            puts("yes");
        else puts("no");
    }
    return 0;
}

融合深搜思想的广搜(单方向扩展):


#include <iostream>  
#include <queue>  
using namespace std;  
#define inf 0x3fffffff  //无穷大
#define M 105  

int r, c, sx, sy, ex, ey, wan[M][M], t, k;//行数,列数,启动x,y终点x,y,弯数,测试数,拐弯数
char map[M][M];//接收控制台字符串
int x_move[4] = {-1, 0, 1, 0};  //四方向移动  
int y_move[4] = {0, 1, 0, -1};  //四方向移动  

struct pos{  
    int x, y;  
};  

void bfs ()  
{  
    int i, j;  
    for (i = 0; i < r; i++)  
        for (j = 0; j < c; j++)  
            wan[i][j] = inf;  
    pos from, next;  
    from.x = sx, from.y = sy;//起点赋值
    wan[from.x][from.y] = -1;  
    queue<pos> q;  
    q.push (from);  
    while (!q.empty())  
    {  
        from = q.front();  
        q.pop();  
        if (from.x == ex && from.y == ey && wan[from.x][from.y] <= k)  
        {  
            puts ("yes");  
            return ;  
        }  
        for (i = 0; i < 4; i++)  
        {  
            next.x = from.x + x_move[i];  
            next.y = from.y + y_move[i]; 
            //whiile循环,单方向直走,该方向上所走过点的拐弯数均为wan[from.x][from.y]+1
            while (!(next.x < 0 || next.y < 0 || next.x >= r || next.y >= c))  
            {  
                if (map[next.x][next.y] == '*')  
                    break;  
                if (wan[next.x][next.y] < wan[from.x][from.y] + 1)//若下一步原来拐弯数比进入该步后(+1)的拐弯数小,说明已遍历过,并且是更优解,则无须再次遍历
                    break;  
                wan[next.x][next.y] = wan[from.x][from.y] + 1;//单方向扩展的每一步都相等,为wan[from.x][from.y]+1
                if (wan[next.x][next.y] > k)//若拐弯数超了,跳出  
                    break;  
                if (wan[next.x][next.y] == k && next.x != ex && next.y != ey)//若拐弯数达到最大,仍与终点不在一条直线上,则跳出
                    break;  
                q.push (next);  
                next.x += x_move[i];  
                next.y += y_move[i];    //单方向优先扩展  
            }  
        }  
    }  
    puts ("no");  
}  

int main()  
{  
    int t, i;  
    scanf ("%d", &t);  
    while (t--)  
    {  
        scanf ("%d%d", &r, &c);  
        for (i = 0; i < r; i++)  
            scanf ("%s", map[i]);  
        scanf ("%d%d%d%d%d", &k, &sy, &sx, &ey, &ex);//到此,结束一趟控制台数据
        sx--, sy--, ex--, ey--;  //我从0开始编号,而题目是从1开始
        bfs ();  
    }  
    return 0;  
}  

JAVA版广搜:


import java.util.Queue;
import java.util.Scanner;
import java.util.concurrent.LinkedBlockingDeque;

public class Main {
	public static void main(String[] args) {
		new Do().go();
	}
}

class Do {
	Queue<Point> que = new LinkedBlockingDeque<Point>();
	Point start = new Point(), end = new Point();;
	int[][] map = new int[103][103];
	int[][] move = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } };
	int high, len, sumTurn;

	public void go() {
		Scanner cin = new Scanner(System.in);
		int n = 0;
		n = cin.nextInt();

		while (n-- > 0) {
			high = cin.nextInt();
			len = cin.nextInt();
			for (int i = 1; i <= high; i++) {
				char[] temp = cin.next().toCharArray();
				for (int j = 1; j <= len; j++) {
					if (temp[j - 1] == '.')
						map[i][j] = Integer.MAX_VALUE;// 每个点拐弯数默认为0
					else
						map[i][j] = -1;// 障碍
				}
			}

			sumTurn = cin.nextInt();
			start.y = cin.nextInt();
			start.x = cin.nextInt();
			map[start.x][start.y] = -1;// 起点拐弯数-1,方便后面当走到下一步时+1,变成正常的初始0转弯数了。
			end.y = cin.nextInt();
			end.x = cin.nextInt();

			// 将迷宫四周围成墙
			for (int i = 0; i <= len + 1; i++) {
				map[0][i] = -1;
				map[high + 1][i] = -1;
			}
			for (int i = 0; i <= high; i++) {
				map[i][0] = -1;
				map[i][len + 1] = -1;
			}

			bfs();//广搜
		}

	}

	private void bfs() {
		if (!que.isEmpty())
			que.clear();
		que.offer(start);
		while (!que.isEmpty()) {
			Point from = que.poll();

			if (from.x == end.x && from.y == end.y && map[from.x][from.y] <= sumTurn) {
				System.out.println("yes");
				return;
			}
			for (int i = 0; i < 4; i++) {
				int nextTurn = map[from.x][from.y] + 1;
				if (nextTurn > sumTurn)// 目标该点新转弯数超范围则退出对from结点的四方向遍历
					break;
				Point to = new Point();// 保存每一次走过的新结点
				to.x = from.x + move[i][0]; // 单方向扩展,直走
				to.y = from.y + move[i][1];
				while (map[to.x][to.y] != -1) {
					if (nextTurn > map[to.x][to.y])// 若目标结点的新转外数不如保存过的拐弯数则遍历下一方向
						break;
					if (nextTurn == sumTurn && to.x != end.x && to.y != end.y)
						break;// 若目标结点拐弯数达到最大仍与终点不在一条直线
					map[to.x][to.y] = nextTurn;// 若拐弯数小于原来的,则赋值
					que.offer(to);

					// 单方向扩展,直走
					Point temp = to;
					to = new Point();
					to.x = temp.x + move[i][0];
					to.y = temp.y + move[i][1];
				}
			}
		}
		System.out.println("no");
	}

	private class Point {
		int x;
		int y;
	}
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值