1 题意
给定一个 n × m n \times m n×m的地图,如:
.XX.1.
..X.2.
2...X.
...XX.
XXXXX.
地图中的符号及其含义如下:
符号 | 含义 |
---|---|
x | 不可达 |
. | 可达,需要花费1s |
n(正整数) | 可达,需要花费ns |
问:从地图的左上角走到右下角至少要花多长的时间,最短路径是怎样的。
链接:link。
2 思路
2.1 BFS & 贪心
命题:
如果地图中没有数字
n
n
n,则直接用
B
F
S
BFS
BFS即可求出最短路径。
如果地图中有数字
n
n
n,则每次都需要从队列中选择一个耗时最短的点往外扩展(贪心)。
证明:略。
2.1.1 时间复杂度分析
每个点至多被访问一次,时间复杂度为 O ( n m ) \mathcal{O}(nm) O(nm)。
2.1.2 实现
在
B
F
S
BFS
BFS中使用优先队列,每次选出耗时最少的点即可。
为了方便处理,先将二维矩阵映射为一维向量:link。
由于要输出路径,所以用
f
a
fa
fa保存某个节点的前继节点,用
s
t
e
p
step
step保存最短耗时。
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=1e2+10;
char mp[N][N];
int dir[4][2]={-1,0,1,0,0,-1,0,1};
int n,m,vis[N*N],fa[N*N],step[N*N];
struct Node{
int ind,step;
Node(int ind=0,int step=0):ind(ind),step(step){}
};
bool operator<(const Node& a,const Node& b){
return a.step>b.step;
}
void BFS(){
priority_queue<Node> qu;
qu.push(Node());
vis[0]=1;
while(!qu.empty()){
Node u=qu.top();qu.pop();
if(u.ind==n*m-1) return;
for(int i=0;i<4;i++){
int x=u.ind/m+dir[i][0],y=u.ind%m+dir[i][1];
if(x>=0&&x<n&&y>=0&&y<m&&mp[x][y]!='X'&&!vis[x*m+y]){
if(mp[x][y]=='.') step[x*m+y]=u.step+1;
else step[x*m+y]=u.step+mp[x][y]-'0'+1;
qu.push(Node(x*m+y,step[x*m+y]));
vis[x*m+y]=1;
fa[x*m+y]=u.ind;
}
}
}
}
void printPath(int ind=n*m-1){
if(ind==0) return;
int find=fa[ind];
int x=ind/m,y=ind%m;
int fx=find/m,fy=find%m;
printPath(find);
if(mp[x][y]=='.') printf("%ds:(%d,%d)->(%d,%d)\n",step[ind],fx,fy,x,y);
else{
int time=mp[x][y]-'0';
printf("%ds:(%d,%d)->(%d,%d)\n",step[ind]-time,fx,fy,x,y);
while(time--) printf("%ds:FIGHT AT (%d,%d)\n",step[ind]-time,x,y);
}
}
int main(){
while(~scanf("%d %d",&n,&m)){
memset(step,0,sizeof(step));
memset(fa,0,sizeof(fa));
memset(vis,0,sizeof(vis));
for(int i=0;i<n;i++) scanf("%s",mp[i]);
BFS();
if(step[n*m-1]==0) printf("God please help our poor hero.\n");
else{
printf("It takes %d seconds to reach the target position, let me show you the way.\n",step[n*m-1]);
printPath();
}
printf("FINISH\n");
}
return 0;
}