题目大意:
题目链接:https://www.luogu.org/problemnew/show/P2199
给出一个图及起点和终点,只要到达终点所在行、列或对角线上且两点之间没有墙就算到达终点。求到达终点的最少步数。
思路:
这道题真的算是蓝题吗。。。
算是很裸的广搜了。
这道题和裸的广搜其实只有一个差别:只要到达终点所在行、列或对角线上且两点之间没有墙就算到达终点。也就是说,这道题有多个终点,到达任意一个即可。
那么就直接跑一边DFS,从原终点往8个方向跑,直到遇见墙为止。那么走到的点就都是成立的终点。
之后就从起点跑BFS。就完全是裸的了。
还有这道题说的数据范围是,所以如果是最坏情况就会有,那么开二维数组就会炸内存。所以要把二维数组压成一个一位数组。
有多组数据,所以时间复杂度为
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int dx[]={0,1,-1,0,0,-1,-1,1,1};
const int dy[]={0,0,0,1,-1,-1,1,-1,1};
int n,m,sx,sy,tx,ty,head,tail,ans[20501],state[20501];
bool a[20501],map[20501],p[20501];
char c;
void dfs(int x,int y,int k) //找终点
{
if (map[(x-1)*m+y]==1) return; //墙
if (x<1||x>n||y<1||y>m) return; //越界
a[(x-1)*m+y]=1; //标记为终点
dfs(x+dx[k],y+dy[k],k);
}
void bfs() //裸的广搜
{
head=0;
tail=1;
state[1]=(sx-1)*m+sy;
while (head<tail)
{
head++;
for (int i=1;i<=4;i++)
{
tail++;
int x=(state[head]-1)/m+1;
int y=state[head]-(x-1)*m; //取出行和列
x+=dx[i];
y+=dy[i]; //移动
if (x<1||x>n||y<1||y>m) //越界
{
tail--;
continue;
}
state[tail]=(x-1)*m+y; //入队
if (p[state[tail]]||map[state[tail]]) //来到过这个点或是墙
{
tail--;
continue;
}
p[state[tail]]=1;
ans[tail]=ans[head]+1;
if (a[state[tail]]) //到达任意一个终点
{
printf("%d\n",ans[tail]);
return;
}
}
}
printf("Poor Harry\n"); //无法到达
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
cin>>c;
while (c=='\n'||c==' ') cin>>c;
if (c=='X') map[(i-1)*m+j]=1;
}
while (1)
{
scanf("%d%d%d%d",&tx,&ty,&sx,&sy);
if (!tx&&!ty&&!sx&&!sy) break;
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
memset(p,0,sizeof(p));
a[(tx-1)*m+ty]=1;
for (int i=1;i<=8;i++)
dfs(tx,ty,i);
if (a[(sx-1)*n+sy]) //特判,本来就在终点上
{
printf("0\n");
continue;
}
bfs();
}
return 0;
}