题目:题目描述
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;
}