dfs+优先队列(poj2312)

Battle City
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 9482   Accepted: 3134

Description

Many of us had played the game "Battle city" in our childhood, and some people (like me) even often play it on computer now. 

What we are discussing is a simple edition of this game. Given a map that consists of empty spaces, rivers, steel walls and brick walls only. Your task is to get a bonus as soon as possible suppose that no enemies will disturb you (See the following picture). 

Your tank can't move through rivers or walls, but it can destroy brick walls by shooting. A brick wall will be turned into empty spaces when you hit it, however, if your shot hit a steel wall, there will be no damage to the wall. In each of your turns, you can choose to move to a neighboring (4 directions, not 8) empty space, or shoot in one of the four directions without a move. The shot will go ahead in that direction, until it go out of the map or hit a wall. If the shot hits a brick wall, the wall will disappear (i.e., in this turn). Well, given the description of a map, the positions of your tank and the target, how many turns will you take at least to arrive there?

Input

The input consists of several test cases. The first line of each test case contains two integers M and N (2 <= M, N <= 300). Each of the following M lines contains N uppercase letters, each of which is one of 'Y' (you), 'T' (target), 'S' (steel wall), 'B' (brick wall), 'R' (river) and 'E' (empty space). Both 'Y' and 'T' appear only once. A test case of M = N = 0 indicates the end of input, and should not be processed.

Output

For each test case, please output the turns you take at least in a separate line. If you can't arrive at the target, output "-1" instead.

Sample Input

3 4
YBEB
EERE
SSTE
0 0

Sample Output

8

#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
char map[305][305];
bool visit[305][305];
int dx[4]={1,-1,0,0},dy[4]={0,0,1,-1};
int m,n,sx,sy,ex,ey;
struct node
{
	int x,y,time;
};
bool operator < (node a,node b)//优先队列中元素的默认比较规则是按元素的值从大到小排序,
                         //取栈顶元素为最大,所以需函数重载 
{
	return a.time>b.time;//从小到大排序采用">",从大到小排序采用"<" 
}
bool ok(int x,int y)
{
	if(x>=0&&x<m&&y>=0&&y<n&&map[x][y]!='S'&&map[x][y]!='R') return true;
	else return false;
}
int bfs()
{
	priority_queue<node> q;//优先队列每部取最小操作次数继续走 
	memset(visit,false,sizeof(visit));
	visit[sx][sy]=true;
	node start={sx,sy,0};
	q.push(start);
	while(!q.empty())
	{
		node s=q.top();
		q.pop();
		if(s.x==ex&&s.y==ey) return s.time;//到达目的地 
		for(int i=0;i<4;i++)
		{
			int xx=s.x+dx[i],yy=s.y+dy[i];//搜索下一个点 
			if(ok(xx,yy)&&!visit[xx][yy])//判断下一个点是否合法 
			{
				visit[xx][yy]=true;//标记已经走过的点 
				int t=0;
				if(map[xx][yy]=='B') t=2;
				else  t=1;
				node next={xx,yy,s.time+t};
				q.push(next);
			}
		}
	}
	return -1;
}
int main()
{
	while(cin>>m>>n&&(m||n))
	{
		for(int i=0;i<m;i++)
		    for(int j=0;j<n;j++)
			{
			    cin>>map[i][j];
			    if(map[i][j]=='Y')//记录起始点 
			    {
			    	sx=i;sy=j;	
				}
				else if(map[i][j]=='T')//终点 
				{
					ex=i;ey=j;
				}
		    }
		cout<<bfs()<<endl;
	}
	return 0;
}

参考:

相信坦克大战大家都玩过吧,本题就是根据这个游戏设计的。坦克要从起点(Y),到目的地(T),坦克不能通过钢墙(S),河(R),可以在空地在行走(E),射击破坏砖墙(B),射击砖墙时不行走且花费一个单位的时间,在空地上行走时也花费一个单位的时间。求坦克从起点到目的地最少花多少时间,不可达输出-1;

很好的一道搜索题。因为考虑到通过砖墙时和空地所花的时间不同,所以不能使用一般的BFS广搜来做。用DFS深搜,你会发现时间复杂非常高,必然会超时(最大是300*300的图)。本题可以使用改进过的广搜或优先队列+bfs 或 记忆化广搜三种方法来解决。

第一种方法:改进过的BFS:

有些节点需要耗费2个单位时间,要想用BFS就得改一下,由于BFS每次只能操作一步,要不就是扩展,要不就是破坏砖墙。所以只需检查该点是不是'B',是的话就得停一步,不是的话,继续扩展,也就是说某些点的扩展慢了一拍,所以从队列里出来的点就判断一下再看执行哪个操作。

从这道题,我也对bfs有了更深的理解,“bfs之所以能最快找到最优解,就是因为它每次操作一步(这里的操作一步,很灵活,例如题目中的破坏砖墙),而while()里面的语句就是一次操作了!”

Cpp代码 
  1. /* 
  2. 这道题中B点需要操作两步,所以遇到B点后不能+2后直接压进队列,需要在原地停一下,不能扩展到其他点,相当于他只能扩展到自身,所以就把自身压进队列里map[x][y]='E'是因为破坏砖墙一次就够了,不然下次,还是'B',不断压进队列,不断在原地停留 
  3. 平常一般是考虑“入队列” 的点,这次要考虑“出队列” 的点是否满足条件! 
  4. */  
  5. #include "iostream"  
  6. #include "queue"  
  7. using namespace std;  
  8.   
  9. char map[301][301];  
  10. bool visit[301][301];  
  11. int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};  
  12. int m,n,sx,sy;  
  13.   
  14. struct node  
  15. {  
  16.     int x,y,time;  
  17. };  
  18. int bfs()  
  19. {  
  20.     int i;  
  21.     node you,start,next;  
  22.     queue<node>q;  
  23.     you.x=sx;  
  24.     you.y=sy;  
  25.     you.time=0;  
  26.   
  27.     q.push(you);  
  28.     visit[sx][sy]=1;  
  29.   
  30.     while(!q.empty())  
  31.     {  
  32.         start=q.front();  
  33.         q.pop();  
  34.         if(map[start.x][start.y]=='B')  //这一步需要停一停  
  35.         {  
  36.             start.time++;  
  37.             map[start.x][start.y]='E';  
  38.             q.push(start);  
  39.         }  
  40.         else  
  41.         {  
  42.             for(i=0;i<4;i++)  
  43.             {  
  44.                 next.x=start.x+dir[i][0];     //搜索下一个点  
  45.                 next.y=start.y+dir[i][1];  
  46.                 if(next.x<0 || next.y<0 || next.x>=m || next.y>=n || map[next.x][next.y]=='R' || map[next.x][next.y]=='S' || visit[next.x][next.y])        //判断下一个点是否合法  
  47.                     continue;  
  48.                 next.time=start.time+1;  
  49.   
  50.                 if(map[next.x][next.y]=='T')    //到达目的地  
  51.                     return next.time;  
  52.                 visit[next.x][next.y]=1;   //标记已经走过的点  
  53.                 q.push(next);  
  54.             }  
  55.         }  
  56.     }  
  57.     return -1;  
  58. }  
  59. int main(void)  
  60. {  
  61.     int i,j;  
  62.     while(scanf("%d %d",&m,&n)==2)  
  63.     {  
  64.         if(m==0 && n==0)  
  65.             break;  
  66.         memset(visit,0,sizeof(visit));     //初始化每个节点的状态  
  67.         for(i=0;i<m;i++)  
  68.         {  
  69.             getchar();  
  70.             for(j=0;j<n;j++)  
  71.             {  
  72.                 scanf("%c",&map[i][j]);  
  73.                 if(map[i][j]=='Y')      //记录起始点  
  74.                 {  
  75.                     sx=i;  
  76.                     sy=j;  
  77.                 }  
  78.             }  
  79.         }  
  80.         printf("%d\n",bfs());  
  81.     }  
  82.     system("pause");  
  83.     return 0;  
  84. }  

第二种方法:优先队列+BFS法
也是用到了广搜的思想,只是在出队时做了处理,利用优先队列让队列中到起点的时间值最小的点先出队。该方法会用到优先队列的STL。

首先需要了解优先队列的使用规则:

优先队列中元素的比较规则默认是按元素的值从大到小排序的,就是说队列中最大的元素总是位于队首,所以出队时,并非按先进先出的原则进行,而是将当前队列中最大的元素出队。这点类似于给队列里的元素进行了从大到小的排序。当然,可以通过重载“<”操作符来重新定义比较规则。

重载“<”操作符的函数可以写在结构体里面,也可以写在结构体外面,写在结构体外面的时候,记得函数的参数要使用引用。。

第一种重载方法:

Cpp代码 
  1. struct node  
  2. {  
  3.     int x,y;  
  4.     int step;  
  5. };  
  6. priority_queue<node>q;       //优先队列中元素的比较规则默认是按元素的值从大到小排序;  
  7.   
  8. bool operator<(const node &a,const node &b) //括号里面是const 而且还必须是引用  
  9. {  
  10.     return a.step>b.step;          //从小到大排序。重载小于号。因为默认是从大到小  
  11. }  

第二种重载方法:

Cpp代码 
  1. struct node  
  2. {  
  3.     int x,y;  
  4.     int time;  //定义一个优先队列  
  5.     friend bool operator<(node a, node b)  
  6.     {     //从小到大排序采用“>”号;如果要从大到小排序,则采用“<”号  
  7.         return a.time> b.time;       //从小到大排序  
  8.     }  
  9. };    
  10. priority_queue<node>q;       //优先队列中元素的比较规则默认是按元素的值从大到小排序;  

切记:从小到大排序采用“>”号;如果要从大到小排序,则采用“<”号;

Cpp代码 
  1. /* 
  2. 优先队列的实现就不用局限每次操作一步了,但每次都取最小操作次数的步来走 
  3. */  
  4. #include "iostream"  
  5. #include "queue"  
  6. using namespace std;  
  7.   
  8. char map[301][301];  
  9. bool visit[301][301];  
  10. int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};  
  11. int m,n,sx,sy;  
  12.   
  13. struct node  
  14. {  
  15.     int x,y,time;  //定义一个优先队列  
  16.     friend bool operator<(node a, node b)  
  17.     {  
  18.         return a.time> b.time;       //从小到大排序  
  19.     }  
  20. };  
  21. int bfs()  
  22. {  
  23.     int i;  
  24.     node you,start,next;  
  25.     priority_queue<node>q;  
  26.     you.x=sx;  
  27.     you.y=sy;  
  28.     you.time=0;  
  29.   
  30.     q.push(you);  
  31.     visit[sx][sy]=1;  
  32.   
  33.     while(!q.empty())  
  34.     {  
  35.         start=q.top();  //取队头指针与普通队列不同(Q.front)  
  36.         q.pop();  
  37.         for(i=0;i<4;i++)  
  38.         {  
  39.             next.x=start.x+dir[i][0];     //搜索下一个点  
  40.             next.y=start.y+dir[i][1];  
  41.             if(next.x<0 || next.y<0 || next.x>=m || next.y>=n || map[next.x][next.y]=='R' || map[next.x][next.y]=='S' || visit[next.x][next.y])        //判断下一个点是否合法  
  42.                 continue;  
  43.             if(map[next.x][next.y]=='B')  //注意此处不要马虎  
  44.                 next.time=start.time+2;  
  45.             else  
  46.                 next.time=start.time+1;  
  47.   
  48.             if(map[next.x][next.y]=='T')    //到达目的地  
  49.                 return next.time;  
  50.             visit[next.x][next.y]=1;        //标记已经走过的点  
  51.             q.push(next);  
  52.         }  
  53.     }  
  54.     return -1;  
  55. }  
  56. int main(void)  
  57. {  
  58.     int i,j;  
  59.     while(scanf("%d %d",&m,&n)==2)  
  60.     {  
  61.         if(m==0 && n==0)  
  62.             break;  
  63.         memset(visit,0,sizeof(visit));     //初始化每个节点的状态  
  64.         for(i=0;i<m;i++)  
  65.         {  
  66.             getchar();  
  67.             for(j=0;j<n;j++)  
  68.             {  
  69.                 scanf("%c",&map[i][j]);  
  70.                 if(map[i][j]=='Y')      //记录起始点  
  71.                 {  
  72.                     sx=i;  
  73.                     sy=j;  
  74.                 }  
  75.             }  
  76.         }  
  77.         printf("%d\n",bfs());  
  78.     }  
  79.     system("pause");  
  80.     return 0;  
  81. }  

第三种方法:记忆化广搜
和优先队列BFS在出队时做处理不同的是,记忆化广搜是在点入队是做处理。记忆化广搜时不必要对点进行标记,只是在入队是注意选择。比如若搜到A点时,要选择比A点时间值大的邻接点入队(不能相等),并更新入队点的时间值。

Cpp代码 
  1. #include<string.h>  
  2. #include<iostream>  
  3. #include<queue>  
  4. using namespace std;  
  5.   
  6. int co,ro,mi,step[305][305];  
  7. char map[305][305],visited[305][305];  
  8. int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};  
  9. typedef struct node  
  10. {  
  11.     int x;  
  12.     int y;  
  13.     int time;  
  14. }node;  
  15. bool judge(int x,int y)  
  16. {  
  17.     if(x<0||y<0||x>=co||y>=ro)  
  18.     {  
  19.         return false;  
  20.     }  
  21.     if(map[x][y]=='S'||map[x][y]=='R')  
  22.     {  
  23.   
  24.         return false;  
  25.     }  
  26.   
  27.     return true;  
  28. }  
  29. void  bfs(int a,int b)  
  30. {  
  31.     int i,x,y,ti;  
  32.     node in,out;  
  33.     queue<node>que;  
  34.     in.x=a;  
  35.     in.y=b;  
  36.     step[a][b]=0;  
  37.     que.push(in);  
  38.     while(!que.empty())  
  39.     {  
  40.         out=que.front();  
  41.         que.pop();  
  42.         visited[out.x][out.y]=0;     
  43.         for(i=0;i<4;i++)  
  44.         {  
  45.             x=out.x+dir[i][0];  
  46.             y=out.y+dir[i][1];  
  47.             if(!judge(x,y))  
  48.                 continue;  
  49.             ti=step[out.x][out.y]+1;  
  50.             if(map[x][y]=='B')  
  51.                 ti++;  
  52.             if(step[x][y]<=ti)  
  53.                 continue;  
  54.             step[x][y]=ti;  
  55.             if(visited[x][y])  
  56.                 continue;  
  57.             visited[x][y]=1;  
  58.             in.x=x;  
  59.             in.y=y;  
  60.             que.push(in);  
  61.         }  
  62.     }  
  63. }  
  64.   
  65. int main()  
  66. {  
  67.     int i,j,a,b,c,d;  
  68.     while(scanf("%d %d",&co,&ro),co+ro)  
  69.     {  
  70.         getchar();  
  71.         for(i=0;i<co;i++)  
  72.             gets(map[i]);  
  73.         for(i=0;i<co;i++)  
  74.             for(j=0;j<ro;j++)  
  75.             {  
  76.                 if(map[i][j]=='Y')  
  77.                 {  
  78.                     a=i;  
  79.                     b=j;  
  80.                 }  
  81.                 if(map[i][j]=='T')  
  82.                 {  
  83.                     c=i;  
  84.                     d=j;  
  85.                 }  
  86.                 step[i][j]=999999;         
  87.             }  
  88.   
  89.             memset(visited,0,sizeof(visited));  
  90.             visited[a][b]=1;  
  91.             bfs(a,b);  
  92.             if(step[c][d]!=999999)  
  93.                 printf("%d\n",step[c][d]);  
  94.             else  
  95.                 printf("-1\n");  
  96.     }  
  97.     return 0;  
  98. }  

猜你喜欢

转载自blog.csdn.net/luojiushenzi/article/details/80204295
今日推荐