解题思路:对优先队列有了更深的理解。以前写优先队列是针对那种有权的最短路问题,搜出来的必定是道路的花费最优解,我们可以用book标记走过的地方,下次不必要再走;而本题由于用了优先队列,走过的点到底要不要再考虑呢?假设上次到达本点拐弯了1次,而现在却拐弯了2次,当然不要本次的这个方法走,相反如果本次用1次,而上次用2次,就要用本次的方法。如果两次的拐弯数一致呢?其实两种都要搜,为什么?因为你进来的方向一定是不同的,而之后你这个方向直直下去如果有终点,你的最小拐弯就是到达本点这么多了,而另外一种方向还要再拐弯一次!
AC代码如下:
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#define inf 0x3f3f3f3f
using namespace std;
struct node
{
int x,y,k,bearing;
friend bool operator < (node a,node b)
{
return a.k>b.k;
}
};
char image[110][110];
int n,m,tx,ty,k,nex[4][2]={0,1,1,0,0,-1,-1,0},book[110][110];
priority_queue<node> Q;
bool judge(int x,int y)
{
if(x<1||x>n||y<1||y>m||image[x][y]=='*')
return true;
return false;
}
bool bfs(int x,int y)
{
if(x==tx && y==ty)
return true;
int i;
node now,xia;
while(!Q.empty())
Q.pop();
book[x][y]=0;
for(i=0;i<4;i++)
{
xia.x=x+nex[i][0];
xia.y=y+nex[i][1];
if(judge(xia.x,xia.y))
continue;
xia.k=0;
xia.bearing=i;
book[x][y]=0;
Q.push(xia);
}
while(!Q.empty())
{
now=Q.top();
Q.pop();
if(now.x==tx && now.y==ty)
return true;
for(i=0;i<4;i++)
{
xia.x=now.x+nex[i][0];
xia.y=now.y+nex[i][1];
if(judge(xia.x,xia.y))
continue;
xia.k=now.k;
xia.bearing=i;
if(xia.bearing!=now.bearing)
xia.k++;
if(xia.k>k || book[xia.x][xia.y]<xia.k) //注意,这里的剪枝当拐弯的次数等同于上一次走过这里的时候不能去掉,因为本次到达这里由于进来的方向不同,能到达的终点也许不同
continue;
if(xia.x==tx && xia.y==ty)
return true;
book[xia.x][xia.y]=xia.k;
Q.push(xia);
}
}
return false;
}
int main()
{
int i,j,t,x,y;
cin>>t;
while(t--)
{
cin>>n>>m;
getchar();
for(i=1;i<=n;i++)
{
cin>>image[i]+1;
for(j=1;j<=m;j++)
book[i][j]=inf;
}
cin>>k>>y>>x>>ty>>tx;
if(bfs(x,y))
cout<<"yes"<<endl;
else
cout<<"no"<<endl;
}
return 0;
}
测试数据:
1
5 5
*....
..*..
.*..*
*....
*....
2 3 5 5 2