目录
前言
第一次手切蓝题,十分高兴,写个题解纪念一下。
更好的阅读环境:题解:AT_abc170_f [ABC170F] Pond Skater - 洛谷专栏
题面
你在一个划分为上下 H 行,左右 W 列的长方形网格中,网格从上到下的第 i 行的从左往右第 j 列被编号为 (i,j) ,如果 ci,j 为 @
,则 (i,j) 不能通过。
你现在在 (x1,y1),你每步可以往上下左右走 1 至 K 格,问你最少需要多少步才能走到 (x2,y2) ,如果不能走到,输出 −1 。
输入先是一行三个整数 H,W,K ,再是一行四个整数 x1,y1,x2,y2,接着是 H×W 的字符矩阵,第 i 行 j 列表示 ci,j 。
题意
给你一个 h 行 w 列的地图 mp,如果 mpi,j 是障碍物,即 mpi,j 是 @
,我们就不能通过 mpi,j ,反之可以通过。你现在在 mpx1,y1,要走到 mpx2,y2,问至少需要几个步骤。在一个步骤内,你可以上下左右移动 1 至 k 个格子。
算法
广度优先搜索和优化。
解法
输入存放
我们可以用一个数据结构存放地图,由于1≤h,w≤106,所以数组存不下。因此,我们考虑使用 vector 或 map。
广度优先搜索实现
初始化
我们可以用一个队列 q 维护待扩展的状态。我们将 (x1,y1) 入队。再定义一个名为 vis 的 vector,用来存放走每个点所需要的步骤。将 visx1,y1 设为 1。 为什么是 1 而不是 0 呢?因为 vis 不仅起到存放步骤的作用,还能用来判定一个点是否走过,如果你把 visx1,y1 设为 0,那么程序就会断定位置 (x1,y1) 没有被走过。由于 visx1,y1 是 1,我们在输出答案 visx2,y2 时要减去一。
广度优先搜索主体
如何广度优先搜索我在这里就不陈述了,几乎就是个板子。我来说一下重要的部分。
扩展
题目的特殊性质在于你一次可以走很多步,我们可以用一个循环枚举走 1 步至走 k 步的情况,一一扩展。
优化
如果你现在想访问的点已经被访问过了,且走到此点的步数比你现在走到此点所需要的步数要少,就直接 break 掉。这个步骤被称为剪枝。
代码
#include<bits/stdc++.h>
using namespace std;
long long h,w,k,xs,ys,xf,yf,dx[]={0,0,1,-1},dy[]={1,-1,0,0};
char iin;
vector<char> mp[1100000];
vector<long long> vis[1100000];
struct node{
long long x,y;
};
void bfs(){
queue<node> q;
q.push({xs,ys});
vis[xs][ys]=1;
while(!q.empty()){
node cur=q.front();
q.pop();
for(int i=0;i<=3;i++){
for(int j=1;j<=k;j++){
node nxt;
nxt.x=cur.x+dx[i]*j;
nxt.y=cur.y+dy[i]*j;
if(nxt.x<1||nxt.x>h||nxt.y<1||nxt.y>w||mp[nxt.x][nxt.y]!='.'){
break;
}
if(vis[nxt.x][nxt.y]&&vis[cur.x][cur.y]>=vis[nxt.x][nxt.y]){
break;
}
if(vis[nxt.x][nxt.y]){
continue;
}
vis[nxt.x][nxt.y]=1;
vis[nxt.x][nxt.y]=vis[cur.x][cur.y]+1;
q.push({nxt.x,nxt.y});
}
}
}
if(vis[xf][yf]){
cout<<vis[xf][yf]-1;
}else{
cout<<-1;
}
}
int main(){
ios::sync_with_stdio(false);
cin>>h>>w>>k>>xs>>ys>>xf>>yf;
for(int i=1;i<=h;i++){
mp[i].push_back('!');
vis[i].push_back(0);
for(int j=1;j<=w;j++){
cin>>iin;
mp[i].push_back(iin);
vis[i].push_back(0);
}
}
bfs();
return 0;
}
评测
请到 AT_abc170_f [ABC170F] Pond Skater - 洛谷 或 F - Pond Skater 评测。