hl uoj1841 走格子

问题描述

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 ,(4 ≤ N,M ≤ 500).表示地图大小。
接下来的N行每行一个长度为M的字符串.表示地形。
输出
你需要输出最少的到达终点的时间,如果不能到达请输出”no”。

样例输入
6 8
########
#.##…F#
#C.##…#
#…#…#
#…##
########

输出样例
4

样例 解释
从C点(3,2)开始,我们首先向左发射传送门,再向下发射传送门,向左进入传送门,到达(5,2),向右发射传送门,向下进入传送门,到达(5,6),向上发射传送门,向右进入传送门,到达(2,6),向右移动,到达F.
在这里插入图片描述
数据范围
50%的数据满足:4<= N,M <=15;
100%的数据满足:4<= N,M <=500;
题解:行动可以分为两种:

  1. 步行,花费一个单位的时间移动到4联通的相邻格子中去.
  2. 使用传送门,指定一个方向的墙的前面的一个格子,步行至最近的一个墙的面前,使用传送门传送.花费的时间为到达最近墙面前花费的时间+1.
    两种行动相组合即可组成任意行动过程.那BFS求出最近的墙的距离,预处理上下左右的第一面墙前的格子.然后建图用DJ跑最短路即可.复杂度为O(MNlog(NM))。

code

#include<bits/stdc++.h>
 using namespace std;
 
int n,m,st,ed,len=0;
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
char s[550][550];
int b[550][550],Left[550][550],Right[550][550],Under[550][550],Above[550][550],vis[511111],d[511111],lin[511111];
struct node{
  int y,v,nxt;
}e[511111*5];
struct Dijk{
	int v,x;
	bool operator <(const Dijk &a) const{
		return a.v < v;
	}
};

inline int num(int i,int j) {return (i-1)*m+j;}

queue<pair<int,int> >q;
void bfs()
{	
  while(q.size())
  {
   int x=q.front().first,y=q.front().second; q.pop();	
   for(int i=0;i<4;i++)
   {
   	int tox=x+dx[i],toy=y+dy[i];
   	if(s[tox][toy]=='.' && !b[tox][toy])
   	{
   	 b[tox][toy]=b[x][y]+1;
	 q.push(make_pair(tox,toy)); 	
	}
   }
  }
} 

void pre()
{
  for(int i=1;i<=n;i++) 
   for(int j=1;j<=m;j++)
   {
    if(s[i][j]=='#') continue;	
   	if(s[i-1][j]=='#') Above[i][j]=num(i,j);
     else Above[i][j]=Above[i-1][j];
    if(s[i][j-1]=='#') Left[i][j]=num(i,j);
     else Left[i][j]=Left[i][j-1];
   }	
  for(int i=n;i>=1;i--)
   for(int j=m;j>=1;j--)
   {
    if(s[i][j]=='#') continue; 	
   	if(s[i+1][j]=='#') Under[i][j]=num(i,j);
     else Under[i][j]=Under[i+1][j];
	if(s[i][j+1]=='#') Right[i][j]=num(i,j);
	 else Right[i][j]=Right[i][j+1];   	
   }
}


void ins(int x,int y,int v) { e[++len].nxt=lin[x]; lin[x]=len; e[len].y=y; e[len].v=v; }
void make_node()
{
   for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)	
	{
      if(s[i][j]=='#') continue;
	  if(s[i+1][j]=='.')  ins(num(i,j),num(i+1,j),1),ins(num(i+1,j),num(i,j),1);
	  if(s[i][j+1]=='.')  ins(num(i,j),num(i,j+1),1),ins(num(i,j+1),num(i,j),1);
	 
	  if(Left[i][j]!=num(i,j))  ins(num(i,j),Left[i][j],b[i][j]);
	  if(Right[i][j]!=num(i,j)) ins(num(i,j),Right[i][j],b[i][j]);
	  if(Under[i][j]!=num(i,j)) ins(num(i,j),Under[i][j],b[i][j]);
	  if(Above[i][j]!=num(i,j)) ins(num(i,j),Above[i][j],b[i][j]);	
	}
} 


priority_queue<Dijk> qe;
void Dijsktra()
{
  qe.push((Dijk){0,st}); d[st]=0;
  while(qe.size())
  {
  	int x=qe.top().x,v=qe.top().v; qe.pop();
  	if(vis[x]) continue; vis[x]=1;
  	for(int i=lin[x];i;i=e[i].nxt)
  	{
  	  int y=e[i].y;	
  	  if(v+e[i].v<d[y]) d[y]=v+e[i].v,qe.push((Dijk){d[y],y});
	}
  }
}
int main()
{
  freopen("cell.in","r",stdin);
  freopen("cell.out","w",stdout);
  cin>>n>>m;
  for(int i=1;i<=n;i++)
   for(int j=1;j<=m;j++)
   {
   	cin>>s[i][j];
	if(s[i][j]=='C') st=num(i,j),s[i][j]='.';
	if(s[i][j]=='F') ed=num(i,j),s[i][j]='.';
	if(s[i][j]=='#') q.push(make_pair(i,j));
   }	
  bfs();
  pre();
  make_node();
  memset(d,10,sizeof(d));
  Dijsktra();
  if(d[ed]==168430090) cout<<"no";
   else cout<<d[ed];
  return 0;	
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值