[solution] JZOJ-5781 秘密通道

JZOJ-5781【秘密通道 】solution

题面

Description

有一副$nm$的地图,有$nm$块地,每块是下列四种中的一种:

墙:用#表示,墙有4个面,分别是前面,后面,左面,右面。

起点:用C表示,为主角的起点,是一片空地。

终点:用F表示,为主角的目的地,是一片空地。

空地:用 . 表示。

其中除了墙不能穿过,其他地方都能走。

主角有以下3种操作:

1.移动到相邻的前后左右的地方,花费一个单位时间。

2.向前后左右其中一个方向发射子弹,子弹沿直线穿过,打在最近的一堵墙的一面,然后墙的这面就会形成一个开口通往秘密通道。同一时间最多只能有两个开口,若出现有3个开口,出现时间最早的开口会立即消失。该操作不用时间。

3.可以从一个与开口相邻的空地跳进去,进入秘密通道,从另外一个开口正对的空地跳出来。这个过程花费一个单位时间。

地图四周都是墙,问主角最少用多少时间从C走到F。C和F只会出现一次。

扫描二维码关注公众号,回复: 2689554 查看本文章

Input

第一行输入两个正整数$n$,$m$。

接下来$n$行,每行$m$个字符描述地图。

Output

输出1个整数,表示最短时间完成路途。如果无解输出nemoguce

Sample Input

Input 1
4 4
####
#.F#
#C.#
####
Input 2
6 8
########
#.##..F#
#C.##..#
#..#...#
#.....##
########
Input 3
4 5
#####
#C#.#
###F#
#####

Sample Output

Output 1
2
Output 2
4
Output 3
nemoguce

Data Constraint

对于50%的数据,$4≤ n,m≤ 15$。

对于100%的数据,$4≤ n,m≤ 500$。

Hint

总共用到8次操作,时间之和为4。如下图所示(图被吃掉了)

1.向左射一枪,在(3,1)的右面出现开口。

2.向下射一枪,在(6,2)的上面出现开口。

3.向左从(3,1)进入秘密通道,从(6,2)中出来,到达(5,2)。用1单位时间。

4.向右射一枪,在(5,7)的左面出现开口,(3,1)右面的开口消失。

5.走进(6,2)的开口,出来到(5,6)。用1单位时间。

6.向上射一枪,在(1,6)的下面出现开口。

7.经过秘密通道,走到(2,6)。用1单位时间。

8.走到终点。用1单位时间。




分割线




思路

这个题乍一眼看上去就像是一个BFS有没有,但是BFS是错误的(虽然数据水到BFS都可以那70分)

至于为什么BFS是错误的,用一个很形象的样例来解释(本人说不清)

#########
#......##
#......##
#......##
#.......#
#.....S.#
#.......#
#.......#
#.......#
#########

这里小X可以先向上和向右打一个子弹,再向右跑到边框,瞬移到上面

手动模拟小X在迷宫中的移动过程,可以发现以下结论

1.这里发射子弹并不能穿墙,所以如果小X在图中与终点不连通的话,那么永远无法到达终点,输出那串字符

2.小X可以在某一个结点$(x_i,y_i)$向周围的某两堵墙分别打一个子弹,再移动到某一个开口处,花1份时间移动到另一个开口,因此小X可以在移动到最近的墙之后花一份时间瞬移到另一个很远的墙边上。

想到这里有没有一点思路了呢?

正解做法

在某一个不是墙的结点$(x_i,y_i)$,向它周围的4个(不是墙的)上下左右结点连一条权值为1的边,向它上下左右4个方向向墙旁边的那个点连一条权值为结点$(x_i,y_i)$与四周墙的最近距离(注意是墙的!!!)的边,模型瞬间转化为了最短路,很神奇有没有!!!

这里最短路可以有很多种实现方式,据说SPFA,Dij+Heap等,不建议使用SPFA因为可能会被卡掉(虽然这里不会)

下面贴上本人巨丑的玄学Dij+Heap代码

Code

294ms,49.33MB

#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #include<queue> #define inf 0x3f3f3f3f #define ll long long using namespace std; char map[503][503]; int n,m; struct Edge{ int si,sj,ti,tj,nxt,w; }edge[2000005]; int si,ti,sj,tj; int head[503][503],book[503][503],dis[503][503],tot=0; const int di[4]={0,0,1,-1},dj[4]={1,-1,0,0}; priority_queue<pair<int,pair<int,int> > > hep; //queue< pair<int,int> > q; int gmin(int a,int b){return a<b?a:b;} void add(int sti,int stj,int toi,int toj,int we){ edge[tot].si=sti; edge[tot].sj=stj; edge[tot].ti=toi; edge[tot].tj=toj; edge[tot].w=we; edge[tot].nxt=head[sti][stj]; head[sti][stj]=tot; tot++; } int main(){ freopen("portal.in","r",stdin); freopen("portal.out","w",stdout); memset(head,-1,sizeof(head)); scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%s",map[i]); for(int i=0;i<n;i++) for(int j=0;j<m;j++){ if(map[i][j]=='C'){ si=i;sj=j; } if(map[i][j]=='F'){ ti=i;tj=j; } } for(int i=1;i<n-1;i++) for(int j=1;j<m-1;j++) if(map[i][j]!='#'){ int ni,nj,minn=inf; for(int k=0;k<4;k++){ ni=i+di[k]; nj=j+dj[k];//cout<<i<<' '<<j<<' '<<k<<endl; if(map[ni][nj]!='#') add(i,j,ni,nj,1); } int nti[4],ntj[4]; for(int k=0;k<4;k++){//cout<<1<<endl; ni=i;nj=j;int cnt=0; while(map[ni][nj]!='#'){ ni+=di[k];nj+=dj[k];cnt++; } ni-=di[k];nj-=dj[k]; nti[k]=ni;ntj[k]=nj; minn=gmin(minn,cnt); } for(int k=0;k<4;k++) add(i,j,nti[k],ntj[k],minn); } memset(dis,0x3f,sizeof(dis)); memset(book,0,sizeof(book)); hep.push(make_pair(0,make_pair(si,sj))); dis[si][sj]=0; /*q.push(make_pair(si,sj)); while(!q.empty()){ int ni=q.front().first,nj=q.front().second; book[ni][nj]=0; q.pop(); for(int i=head[ni][nj];i!=-1;i=edge[i].nxt){ int nti=edge[i].ti,ntj=edge[i].tj,w=edge[i].w; if(dis[nti][ntj]>dis[ni][nj]+w){ dis[nti][ntj]=dis[ni][nj]+w; if(!book[nti][ntj]){ q.push(make_pair(nti,ntj)); book[nti][ntj]=1; } } } }*/ while(!hep.empty()){ int ni=hep.top().second.first,nj=hep.top().second.second;//cout<<"pp"<<endl; hep.pop(); if(book[ni][nj]==0){ book[ni][nj]=1; for(int i=head[ni][nj];i!=-1;i=edge[i].nxt){ int nti=edge[i].ti,ntj=edge[i].tj,w=edge[i].w; if(dis[nti][ntj]>dis[ni][nj]+w){ dis[nti][ntj]=dis[ni][nj]+w; hep.push(make_pair(0-dis[nti][ntj],make_pair(nti,ntj))); } } } } if(dis[ti][tj]==inf) printf("nemoguce\n"); else printf("%d\n",dis[ti][tj]); //for(int i=0;i<tot;i++){ // cout<<i<<' '<<edge[i].si<<' '<<edge[i].sj<<' '<<edge[i].ti<<' '<<edge[i].tj<<' '<<edge[i].w<<endl; //} //for(int i=0;i<n;i++){ // for(int j=0;j<m;j++){ // cout<<map[i][j]<<' '; // }cout<<endl; //} return 0; }

---恢复内容结束---

猜你喜欢

转载自www.cnblogs.com/theOldChun/p/9458415.html
今日推荐