算法学习笔记二:逃离迷宫from中南复试

题目:题目描述
PIPI被困在一个迷宫中了!
  给定一个m × n (m行, n列)的迷宫,迷宫中有两个位置,PIPI想从迷宫的一个位置走到另外一个位置,当然迷宫中有些地方是空地,PIPI可以穿越,有些地方是障碍,她必须绕行,从迷宫的一个位置,只能走到与它相邻的4个位置中,当然在行走过程中,PIPI不能走到迷宫外面去。令人头痛的是,PIPI是个没什么方向感的人,因此,她在行走过程中,不能转太多弯了,否则她会晕倒的。我们假定给定的两个位置都是空地,初始时,PIPI所面向的方向未定,她可以选择4个方向的任何一个出发,而不算成一次转弯。PIPI能从一个位置走到另外一个位置吗?
输入
  第1行为一个整数t (1 ≤ t ≤ 100),表示测试数据的个数,接下来为t组测试数据,每组测试数据中,
  第1行为两个整数m, n (1 ≤ m, n ≤ 100),分别表示迷宫的行数和列数,接下来m行,每行包括n个字符,其中字符’.‘表示该位置为空地,字符’*'表示该位置为障碍,输入数据中只有这两种字符,每组测试数据的最后一行为5个整数k, x1, y1, x2, y2 (1 ≤ k ≤ 10, 1 ≤ x1, x2 ≤ n, 1 ≤ y1, y2 ≤ m),其中k表示PIPI最多能转的弯数,(x1, y1), (x2, y2)表示两个位置,其中x1,x2对应列,y1, y2对应行。
输出
 每组测试数据对应为一行,若PIPI能从一个位置走到另外一个位置,输出“yes”,否则输出“no”。
样例输入
2
5 5
…**
*..



1 1 1 1 3
5 5

*.
*.


*…
2 1 1 1 3
样例输出
no
yes

首先,我的思路:bfs遍历,用结构体记录转弯次数以及上一个点的走向。然后每次判断是否转弯。一直WA。调试了一天才发现问题:代码问题:终点路径上某一点A可能可以由多条路径到达,而如果有一条路径S先到达点A,将A的visited数组变为true。
但是如果沿路径S走会超出规定的转弯次数。此时当正确的那条路径到达时会因为visited判断而无法到达此点。

自己写的错误代码

//本代码问题:终点路径上某一点A可能可以由多条路径到达,而如果有一条路径S先到达点A,将A的visited数组变为true。
//但是如果沿路径S走会超出规定的转弯次数。此时当正确的那条路径到达时会因为visited判断而无法到达此点。
/*用例:
2
5 5
.....
.*.*.
.....
.*.*.
.....
1 1 1 3 4
应输出yes 
*/ 
#include
#include 
#include
using namespace std;
bool visited[110][110];
struct Node
{
	int y,x;
	int k;//k记录转向次数 
	int f;//f记录上一点的走向以便与本点的走向比较判断是否转弯 ps:+1为上-1为下+2为右-2为左 
};
Node up(Node cur)
{
	Node next;
	next.y=cur.y-1;
	next.k=cur.k;
	next.x=cur.x;
	return next;
}
Node down(Node cur)
{
	Node next;;
	next.y=cur.y+1;
	next.k=cur.k;
	next.x=cur.x;
	return next;
}
Node left(Node cur)
{
	Node next;
	next.y=cur.y;
	next.k=cur.k;
	next.x=cur.x-1;
	return next;
}
Node right(Node cur)
{
	Node next;
	next.y=cur.y;
	next.k=cur.k;
	next.x=cur.x+1;
	return next;
}
bool judge(Node cur,char st[110][110],int m,int n)//y:行 x:列 
{
	if(cur.x<1||cur.y<1||cur.x>n||cur.y>m)
		return false;
	if(st[cur.y][cur.x]=='*')
		return false;
	if(visited[cur.y][cur.x]==true)
		return false;

	return true;
}
int bfs(Node sta,Node end,int k,char st[110][110],int m,int n)
{
	queue<Node> q;
	visited[sta.y][sta.x]=true;
	Node cur,next,ss;
	cur=sta;
	q.push(cur);
	while(!q.empty())
	{
		cur=q.front();
		q.pop();
		if(cur.x==end.x&&cur.y==end.y)
		{
			return 1;
		}
		
		ss=up(cur);
		if(judge(ss,st,m,n))
		{
			next=up(cur);  
			visited[next.y][next.x]=true;
			if(cur.f!=1)
				next.k++;
			if(next.k<=k+1)
			{
				next.f=1;
				q.push(next); 
			}
				
		}
		ss=down(cur);
		if(judge(ss,st,m,n))
		{  
			next=down(cur);
			visited[next.y][next.x]=true;
			if(cur.f!=-1)
				next.k++;
			if(next.k<=k+1)
			{
				next.f=-1;
				q.push(next); 
			}			
		}
		ss=left(cur);
		if(judge(ss,st,m,n))
		{
			next=left(cur);
			visited[next.y][next.x]=true;
			if(cur.f!=-2)
				next.k++;
			if(next.k<=k+1)
			{
				next.f=-2;
				q.push(next);
			}	 
		}
		ss=right(cur);
		if(judge(ss,st,m,n))
		{
			next=right(cur);
			visited[next.y][next.x]=true;
			if(cur.f!=2)
				next.k++;
			if(next.k<=k+1)
			{
				next.f=2;
				q.push(next); 
			}	
		}				
	}
	return -1;
	
}
int main()
{
	int t,a;
	cin>>t;
	for(a=0;a<t;a++)
	{
		int k,x1,x2,y1,y2,m,n,i=0,j,ts=0;;//y:行 x:列 
		char st[110][110];
		memset(visited,false,sizeof(visited));
		cin>>m>>n;
		for(i=1;i<=m;i++)
			for(j=1;j<=n;j++)
				cin>>st[i][j];
		cin>>k>>x1>>y1>>x2>>y2;
		if(k<0)
			k=0;
		
		Node sta,end;
		sta.y=y1;
		sta.x=x1;
		sta.k=0;
		sta.f=0;
		end.y=y2;
		end.x=x2;
		if(st[y1][x1]=='*'||st[y2][x2]=='*')//起点终点不能是* 
			cout<<"no"<<endl;
		else
		{
			ts=bfs(sta,end,k,st,m,n);
			if(ts==1)
				cout<<"yes"<<endl;
			if(ts==-1)
				cout<<"no"<<endl;
		}
	
	}
	return 0;
}

此时通过查询发现可以用优先队列来解决,优先将转弯数更小的点出队列。
但是还有一个问题:有时候权值一样的点也是不同的,应为此题权值是转弯数,而你走到一个权值相同的点方向是不一样的,但优先队列不能判断出这点不同,最佳的方向走必经的那个点可能已经其他方向来的走过而不能再走。所以我又被卡住了…

经过一番搜索 发现了更好的思路

/*在这里我们可以从起点开始取一个方向,然后在第i(i初始为0)次转弯的
时候将该方向的点都走完,就是在转一次弯的情况下,可以走到哪些点,
然后标记,如果下次走的时候不是标记的点,那么肯定需要转弯了 */
#include
#include
//#include
#include
#include
#define N 105
using namespace std;
bool visited[N][N];
char map[N][N];
int to[4][2]={{1,0},{-1,0},{0,-1},{0,1}}; //很巧妙 
int ans,m,n,x1,x2,y1,y2,k;
struct Node
{
	int x,y;
	int k;//k记录转向次数 		
};
queue<Node> q;
int bfs()
{
	int i;
	Node cur,next,ss;
	cur.x=x1;
	cur.y=y1;
	cur.k=-1;
	visited[x1][y1]=1;
	q.push(cur);
	while(!q.empty())
	{
		cur=q.front();
		q.pop();
		if(cur.x==x2&&cur.y==y2&&cur.k<=k)
			return 1;
		for(i=0;i<4;i++)
		{	
			next.x=cur.x+to[i][0];
			next.y=cur.y+to[i][1];
			while(next.x>=1 && next.x<=n && next.y<=m && next.y>=1 && map[next.x][next.y]!='*'){
				if(visited[next.x][next.y]==0){
					next.k=cur.k+1;
					visited[next.x][next.y]=1;
					q.push(next);
				}	
				ss.x=next.x+to[i][0];//一直向一个方向走 
				ss.y=next.y+to[i][1];
				next=ss;
			}
		}
	}
	return 0;	
}
int main()
{
	int t,i,j;
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)	scanf("%s",map[i]+1);
		scanf("%d%d%d%d%d",&k,&y1,&x1,&y2,&x2);
		while(!q.empty())	q.pop(); //注意要初始化
		memset(visited,0,sizeof(visited));
		ans=bfs();
		if(ans)	printf("yes\n");
		else printf("no\n");
	}
	return 0;
}

你可能感兴趣的:(算法题)