HDU 1072 记忆化搜 DFS+BFS

题目大意:

0为墙1为路2为起点3为终点4为炸弹

走到任意一个炸弹都可以将所有炸弹重置倒计时6minutes

每走一个位置需要1minutes

问从2到3需要的最少时间

DFS法更快。

BFS法好理解。

思路:

两种方法都需理解一点:

同一个炸弹位置当第二次走到时说明已不是最优解。

BFS法:

处理走到同一个炸弹位置方法:第一次走到炸弹的位置时,将该炸弹设置为0(即墙),将不会再一次走到此处。

然后就是BFS了。

DFS法:

记忆化搜索。

记录走过该点的时间与炸弹剩余爆炸时间。

剪枝:去掉同样走到该位置时所用时间更久和剩余爆炸时间更短的路。

由于BFS法在遍历过程中遍历了更多没必要的路,所以更慢些。

BFS代码

#include<cstdio>
#include<iostream>
#include<queue>
#define N 10
using namespace std;
int map[N][N],n,m;
int dir[4][2]=
{
    {0,1},  /*向右*/
    {0,-1}, /*向左*/
 {-1,0},  /*向下*/
 {1,0}  /*向上*/
};
struct Node
{
   int x,y;//记录坐标 
   int step,time;//步数和时间 
}start;
void BFS()
{
   queue<Node>q;//队列实现
   Node q1,q2;//交换值,相当于temp 
   q.push(start);//将start放入队列
   
   //队列为空时说明 1.已经扫描到结果
   //2.完全扫描结束(没找到结果)
   while(!q.empty())
   {
      int i;
      q1=q.front();//将队头的数据拿出来
      q.pop();//将队头弹出
      //开始搜索上下左右四个方向 
      for(i=0;i<4;i++)
      {
         q2.x=q1.x+dir[i][0];
         q2.y=q1.y+dir[i][1];
         q2.step=q1.step+1;
         q2.time=q1.time-1;
         //判断走这一步是否已经超出矩阵范围 
          //确定此步不是走过的(或墙)或者炸弹时间已到 
         if(q2.x>=0&&q2.y>=0&&q2.x<n&&q2.y<m&&map[q2.x][q2.y]!=0&&q2.time>0)
         {
            //说明找到答案,结束搜索 
            if(map[q2.x][q2.y]==3)
            {
               printf("%d\n",q2.step);
               return;                    
            }
            else if(map[q2.x][q2.y]==4)
            {//碰到时间调整器,可以恢复时间 
                 q2.time=6;
                 map[q2.x][q2.y]=0; //标记已经走过   
            }  
            q.push(q2);//将这一步放进队列                               
         }
      } 
   }  
   //队列扫完都没搜到答案,说明答案不存在
   printf("-1\n");
   return;
}
int main()
{
    int i,j,T;
    scanf("%d",&T);
    while(T--)
    {
       scanf("%d %d",&n,&m);
       for(i=0;i<n;i++)
         for(j=0;j<m;j++)
         {
            scanf("%d",&map[i][j]);
            if(map[i][j]==2)
            {
               start.x=i;
               start.y=j;
               start.step=0;
               start.time=6;//时间初始化为6 
            }
         }
         BFS(); 
    }
    return 0;
}

DFS代码

但其实,炸弹重置点不要重复走,因为,走到炸弹重置点时时间就会被设置为最大时间,当重新返回时时间又设成最大,但此时已走的步数肯定增加了,所以如果存在较优解的话那么肯定在第一次到这点后就可以找到较优解,这也是代码中剪枝的原理,只是将这种思想扩展到普通点而已,所以采用记忆化搜。

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
int to[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int map[10][10];
int step[10][10];
int time[10][10];
int ans;
int n,m,sx,sy;
void dfs(int x,int y,int nowstep,int bombtime){
	if(x<1||y<1||x>n||y>m||map[x][y]==0)
	return ;
	if(bombtime==0||nowstep>=ans)
	return ;
	if(map[x][y]==3)
	{
		ans=min(ans,nowstep);
		return ;
	}
	if(map[x][y]==4)
	bombtime=6;
	//最优剪枝
	//下面的这个剪枝很重要,不剪就会超时
	//从当前点x,y走到下一个可能点的距离大于从其他途径到nx,ny的距离,且到nx,ny点时的剩余时间大于由x,y点到nx,ny点后的剩余时间,就跳过
	//这是因为结点可重复访问所以本身没标记,那么当上述条件满足时,由nx,ny开始的最优解已经求过(存在或者不存在),所以不需要再重复求了。

	if(nowstep>=step[x][y]&&time[x][y]>=bombtime)
	return ;
	step[x][y]=nowstep;
	time[x][y]=bombtime;
	int nx,ny;
	for(int i=0;i<4;i++)
	{
		nx=x+to[i][0];
		ny=y+to[i][1];
		dfs(nx,ny,nowstep+1,bombtime-1);
	}
	
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			scanf("%d",&map[i][j]);
			step[i][j]=0x3f3f3f3f;
			time[i][j]=0;
			if(map[i][j]==2)
			{
				sx=i;
				sy=j;
			}
		}
		ans=0x3f3f3f3f;
		dfs(sx,sy,0,6);
		if(ans==0x3f3f3f3f)
		printf("-1\n");
		else 
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Harington/article/details/82015033