HDU 5335 Walk Out (搜索+贪心,超详解)经典

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5335


题面:

Walk Out

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2355    Accepted Submission(s): 459


Problem Description
In an nm maze, the right-bottom corner is the exit (position (n,m) is the exit). In every position of this maze, there is either a 0 or a 1 written on it.

An explorer gets lost in this grid. His position now is (1,1) , and he wants to go to the exit. Since to arrive at the exit is easy for him, he wants to do something more difficult. At first, he'll write down the number on position (1,1) . Every time, he could make a move to one adjacent position (two positions are adjacent if and only if they share an edge). While walking, he will write down the number on the position he's on to the end of his number. When finished, he will get a binary number. Please determine the minimum value of this number in binary system.
 

Input
The first line of the input is a single integer T (T=10) , indicating the number of testcases.

For each testcase, the first line contains two integers n and m (1n,m1000) . The i -th line of the next n lines contains one 01 string of length m , which represents i -th row of the maze.
 

Output
For each testcase, print the answer in binary system. Please eliminate all the preceding 0 unless the answer itself is 0 (in this case, print 0 instead).
 

Sample Input
  
  
2 2 2 11 11 3 3 001 111 101
 

Sample Output
  
  
111 101
 

Author
XJZX
 

Source
 

解题:
    比赛的时候,想了N久,都没想出来,一直想着深搜会跪,都没想着广搜。看了好多题解,都没看懂,决定自己写一发。
    大致思路是,如果起始点是1的话,那么就是去广搜字典序最小的那条路。路径长为n+m-1,(因为起始为1之后,只会往下或往右走)通过标记VIS数组,从而达到了每个点只访问一次的目的,故时间复杂度(n*m)。因为等长的路径分布在斜线上(向下或向右都是到达同一条斜线),每次逐斜线扫过去。如果通过之前的点,在当前斜线可以到达0,那么就会选择0,没有0可选的情况下,才会选择1。(贪心策略)。
    如果起始点不是1,那么就取广搜离起始点曼哈顿距离最远,也就是离目标点最近的1。因为,可能存在多个,那么全部加入队列即可。之后,就重复以上过程即可。

代码:
//如果之前的搜索最远的1用的是深搜,那么需扩栈,如果是广搜就不必了
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
//map存原图
char map[1005][1005];
//vis访问标记,able标识该点是否可以从0可达
bool vis[1005][1005],able[1005][1005];
//path记录路径,cnt为路径的下标
int t,n,m,xx,yy,path[2010],cnt=0;
//dir为四个方向
int dir[4][2]={-1,0,0,1,1,0,0,-1};
//每个节点,即位置
struct node
{
	int x,y;
	node (int xx,int yy)
	{
		x=xx;
		y=yy;
	}
};
//判断是否越界
bool Inside(int x,int y)
{
	if(x>=0&&x<n&&y>=0&&y<m)
		return true;
	return false;
}
queue <node> q;
//搜索能够通过0达到的位置
void bfss()
{
   int tx,ty;
   q.push(node(0,0));
   node tmp(0,0);
   while(!q.empty())
   {
     tmp=q.front();
	 q.pop();
	 //4个方向扩展
	 for(int i=0;i<4;i++)
	 {
		 tx=tmp.x+dir[i][0];
		 ty=tmp.y+dir[i][1];
		 if(Inside(tx,ty)&&!vis[tx][ty])
		 {
			 vis[tx][ty]=1;
			 if(map[tx][ty]=='0')
			 {
				 //able代表该点可达
				 able[tx][ty]=1;
				 q.push(node(tx,ty));
			 }
		 }
	 }
   }
}
//qe存过程中的点,v0存0的位置,v1存1的位置
queue <node> qe;
vector <node> v0;
vector <node> v1;
bool rea[1005][1005];
//搜索最短,且字典序最小的路径
void bfs()
{
	while(!qe.empty())
		qe.pop();
	int tx,ty,sz;
	bool sign=false;
	//rea访问标记
	memset(rea,0,sizeof(rea));
	node tmp(0,0);
	while(1)
	{
		//如果之前出现了可达的0
		if(v0.size())
		{
			//那么当前路径位置就取0
			path[cnt++]=0;
			sz=v0.size();
			for(int i=0;i<sz;i++)
				qe.push(v0[i]);
		}
		//如果没有出现过0,都是1
		else
		{
			//那么当前路径位置就取1
            path[cnt++]=1;
			sz=v1.size();
			for(int i=0;i<sz;i++)
				qe.push(v1[i]);
		}
		//清空
		v0.clear();
		v1.clear();
		//将qe中的点的下一步位置存到v0,v1中
		while(!qe.empty())
		{
			tmp=qe.front();
			qe.pop();
			//向下
			tx=tmp.x+1;
			ty=tmp.y;
			//因为rea数组,每个点最多进入队列一次
			if(Inside(tx,ty)&&(!rea[tx][ty]))
			{
				rea[tx][ty]=1;
				if(map[tx][ty]=='0')
					v0.push_back(node(tx,ty));
				else
					v1.push_back(node(tx,ty));
			}
			//向右
			tx=tmp.x;
			ty=tmp.y+1;
	        if(Inside(tx,ty)&&(!rea[tx][ty]))
			{
				rea[tx][ty]=1;
				if(map[tx][ty]=='0')
					v0.push_back(node(tx,ty));
				else
					v1.push_back(node(tx,ty));
			}

		}
		//没有点了,说明已到达终点
		if((v0.size()==0)&&(v1.size()==0))
			break;
	}
}
int main()
{
	int minn=0,x,y,z=1;
	bool flag;
	scanf("%d",&t);
	while(t--)
	{
		//读入
		cnt=0;
		scanf("%d%d",&n,&m);
		for(int i=0;i<n;i++)
		{
            scanf("%s",map[i]);
		}
		//起点为1,从起点开始寻路
		if(map[0][0]=='1')
		{
           v1.push_back(node(0,0));
		}
		//起点为0,先找到最远的点们,然后开始寻路
		else
		{
		   memset(vis,0,sizeof(vis));
		   memset(able,0,sizeof(able));
           bfss();
		   vis[0][0]=able[0][0]=1;
		   minn=0;
		   flag=false;
		   //从离终点最近的斜线开始扫
         for(int i=1;i<=m;i++)
		 {
			 for(int j=1;j<=i;j++)
			 {
				 x=n-j;
				 y=m-(i+1-j);
				 //只有x+y最大的且合法的点,才会被加入队列(此处为向量)
				 if(x+y>=minn)
				 {
					 //该点左边或者上边是可达的
					 if((Inside(x-1,y)&&able[x-1][y])||(Inside(x,y-1)&&able[x][y-1]))
					 {
						 minn=x+y;
						 if(map[x][y]=='1')
					     v1.push_back(node(x,y));
					}
				 }
				 else
				 {
					 flag=true;
					 break;
				 }
			  }
			 if(flag)break;
		   }
		}
		//
		if(!flag)
		{
			//扫第一条边那的斜线
			for(int i=n-1;i>=1;i--)
			{
				for(int j=1;j<=i;j++)
				{
					if(x+y>=minn)
					{
						x=i-j;
					    y=j-1;
					    if((Inside(x-1,y)&&able[x-1][y])||(Inside(x,y-1)&&able[x][y-1]))
					    {
                          minn=x+y;
						  if(map[x][y]=='1')
							  v1.push_back(node(x,y));
					    }
					}
					else
					{
						flag=true;
						break;
					}
				}
			}
		}
		//如果有全都为0的路径,直接输出0
		if(v1.size()==0)
		{
			printf("0\n");
			continue;
		}
		//寻路
		bfs();
		//输出
		for(int i=0;i<cnt;i++)
			printf("%d",path[i]);
		printf("\n");
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值