[CSP-S模拟测试]:走格子(模拟+BFS+Dijkstra)
题目描述
$CYJ$想找到他的小伙伴$FPJ$,$CYJ$和$FPJ$现在位于一个房间里,这个房间的布置可以看成一个$N$行$M$列的矩阵,矩阵内的每一个元素会是下列情况中的一种:
$1.$障碍区域—这里有一堵墙(用$'\#'$表示)。
$2.$这是$CYJ$最开始在的区域(用$'C'$表示)。
$3.$这是$FPJ$在的区域(用$'F'$表示)。
$4.$空区域(用$'.'$表示)。
$CYJ$携带了一个所谓传送枪的东西,这是一把可以创造传送门的枪械,在每一次行动中,他可以选择下列操作中的一项:
$1.$移向一个相邻的格子中(上,下,左,右,不能移到墙在的格子里)。这个操作要消耗一个单位的时间。
$2.$转向一个墙(不需要相邻,只需面向即可),向其发射传送门,传送门会留在墙内面向你的地方(至多只能同时存在两扇传送门),若墙上已经有两扇传送门,而你发射了第三扇,那么最初发射的那一扇会消失。同时,你无法在一个位置制造两扇传送门(这个操作不会耗费时间)。
$3.$如果他与一块墙壁相邻且面前有一扇传送门,那么他可以移动到另一扇传送门前方的格子。这个操作会耗费一个单位的时间。
$CYJ$想要知道自己最少需要多少时间才能够从起点($'C'$)到达终点($'F'$)。
请注意:我们保证地图边缘会是一圈墙壁且一定存在$'C'$,$'F'$。
输入格式
第一行输入两个正整数$N$和$M$,表示地图大小。
接下来的$N$行每行一个长度为$M$的字符串,表示地形。
输出格式
你需要输出最少的到达终点的时间,如果不能到达请输出$"no"$。
样例
样例输入1:
4 4
####
#.F#
#C.#
####
样例输出1:
2
样例输入2:
6 8
########
#.##..F#
#C.##..#
#..#...#
#.....##
########
样例输入2:
4
样例输入3:
4 5
#####
#C#.#
###F#
#####
样例输出3:
no
数据范围与提示
$50\%$的数据满足:$4\leqslant N,M\leqslant 15$;
$100\%$的数据满足:$4\leqslant N,M\leqslant 500$;
题解
这道题教练出的有些尴尬,我在个人简介中说过我在多个信竞队都混过,然后就有了这道题的故事。
大概在半年前,$Akoasm$大神出了这道题,他想卖版权,我帮他审的这道题,然而就在半年后……
顺便附一个$Akoasm$大神的链接:$\mathfrak{Akoasm}$。
扯淡完了,言归正转。
这道题应该是我审过的$Akoasm$大神出的最好的一道题了。
题目很新颖,很难想到正解,好多人都想到的是爆搜+剪枝,至于少数几个人想到了正解。
首先,我们通过$BFS$预处理出距离最近墙的距离,在预处理出上下左右的第一面墙前的格子在哪里,建图,在图上跑$Dijkstra$即可。
就这么简单,主要是很难想得到。
时间复杂度:$\Theta(n\times m\log(n\times m))$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h>
using namespace std;
struct rec
{
int nxt;
int to;
int w;
}e[2222222];
int head[300000],cnt;
int n,m;
char ch[600];
int st,ed;
bool Map[600][600];
int bs[600][600],u[600][600],d[600][600],l[600][600],r[600][600];
bool vis[300000];
int dis[300000];
queue<pair<int,int> > que;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
void add(int x,int y,int w)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
e[cnt].w=w;
head[x]=cnt;
}
void insert(int x,int y,int w)
{
if(Map[x][y]&&!bs[x][y])
{
bs[x][y]=w;
que.push(make_pair(x,y));
}
}
void pre_bfs()
{
while(!que.empty())
{
pair<int,int> flag=que.front();
que.pop();
insert(flag.first-1,flag.second,bs[flag.first][flag.second]+1);
insert(flag.first,flag.second-1,bs[flag.first][flag.second]+1);
insert(flag.first+1,flag.second,bs[flag.first][flag.second]+1);
insert(flag.first,flag.second+1,bs[flag.first][flag.second]+1);
}
}
void pre_work()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(Map[i][j])
{
if(!Map[i][j-1])l[i][j]=(i-1)*m+j;
else l[i][j]=l[i][j-1];
if(!Map[i-1][j])u[i][j]=(i-1)*m+j;
else u[i][j]=u[i-1][j];
}
for(int i=n;i;i--)
for(int j=m;j;j--)
if(Map[i][j])
{
if(!Map[i][j+1])r[i][j]=(i-1)*m+j;
else r[i][j]=r[i][j+1];
if(!Map[i+1][j])d[i][j]=(i-1)*m+j;
else d[i][j]=d[i+1][j];
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(Map[i][j])
{
if(Map[i][j+1]){add((i-1)*m+j,(i-1)*m+j+1,1);add((i-1)*m+j+1,(i-1)*m+j,1);}
if(Map[i+1][j]){add((i-1)*m+j,i*m+j,1);add(i*m+j,(i-1)*m+j,1);}
if(u[i][j]!=(i-1)*m+j)add((i-1)*m+j,u[i][j],bs[i][j]);
if(d[i][j]!=(i-1)*m+j)add((i-1)*m+j,d[i][j],bs[i][j]);
if(l[i][j]!=(i-1)*m+j)add((i-1)*m+j,l[i][j],bs[i][j]);
if(r[i][j]!=(i-1)*m+j)add((i-1)*m+j,r[i][j],bs[i][j]);
}
}
void Dij(int x)
{
memset(dis,0x3f,sizeof(dis));
q.push(make_pair(0,x));
dis[x]=0;
while(!q.empty())
{
int flag=q.top().second;
q.pop();
if(vis[flag])continue;
vis[flag]=1;
for(int i=head[flag];i;i=e[i].nxt)
if(dis[e[i].to]>dis[flag]+e[i].w)
{
dis[e[i].to]=dis[flag]+e[i].w;
q.push(make_pair(dis[e[i].to],e[i].to));
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",ch+1);
for(int j=1;j<=m;j++)
{
if(ch[j]=='C')st=(i-1)*m+j;
if(ch[j]=='F')ed=(i-1)*m+j;
if(ch[j]=='#')que.push(make_pair(i,j));
if(ch[j]=='C'||ch[j]=='F'||ch[j]=='.')Map[i][j]=1;
}
}
pre_bfs();
pre_work();
Dij(st);
printf("%d",dis[ed]);
return 0;
}
rp++