问题描述
小明每天都要练功,练功中的重要一项是梅花桩。
小明练功的梅花桩排列成 n 行 m 列,相邻两行的距离为 1,相邻两列的距离也为 1。
小明站在第 1 行第 1 列上,他要走到第 n 行第 m 列上。小明已经练了一段时间,他现在可以一步移动不超过 d 的距离(直线距离)。
小明想知道,在不掉下梅花桩的情况下,自己最少要多少步可以移动到目标。
输入格式
输入的第一行包含两个整数 n, m,分别表示梅花桩的行数和列数。
第二行包含一个实数 d(最多包含一位小数),表示小明一步可以移动的距离。
输出格式
输出一个整数,表示小明最少多少步可以到达目标。
样例输入
3 4
1.5
样例输出
3
评测用例规模与约定
对于 30% 的评测用例,2 <= n, m <= 20,1 <= d <= 20。
对于 60% 的评测用例,2 <= n, m <= 100,1 <= d <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= d <= 100。
思路:
考虑 B F S BFS BFS,将左上角坐标 ( 1 , 1 ) (1,1) (1,1)入队,令 d [ 1 ] [ 1 ] d[1][1] d[1][1] 处为0。不断将队头出队,并将与队头坐标 ( i , j ) (i,j) (i,j)距离 d i s < d dis<d dis<d 的所有坐标入队,并将其坐标对应的 d [ x ] [ y ] d[x][y] d[x][y] 标记为 d [ i ] [ j ] + 1 d[i][j]+1 d[i][j]+1 ,直到队列为空,此时, d [ n ] [ m ] d[n][m] d[n][m] 处即为答案。
但这并不好实现,在找与队头坐标 ( i , j ) (i,j) (i,j)距离 d i s < d dis<d dis<d 的所有坐标的过程中,如果搜索全图,必然TLE,所以我们要尽可能贪心地找最外围的情况,因为搜索的范围是一个圆心在左上角的 1 4 \dfrac {1}{4} 41圆,所以我们可以采用圆内贪心:
先搜索圆的右边界:
t
y
=
y
+
(
i
n
t
)
d
ty = y + (int)d
ty=y+(int)d(由于不能掉下梅花桩,我们必须取
y
+
(
i
n
t
)
d
y + (int)d
y+(int)d为最右的边界),这肯定能到达,之后采取贪心法找边界:我们从上到下去找圆的最右边界(当然是整数点),也就是一直tx ++
,但是总会有一个时刻点会超出圆的右边界,这时我们需要缩小点的横坐标,即ty --
,直到满足圆内,我们继续tx ++
,这样一定能找到所有最外围情况的点
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1004
int dis[N][N];
int n,m;
double d;
queue< pair<int,int> >q;
void bfs(){
q.push(make_pair(1, 1));
while(!q.empty()){
pair<int,int> t = q.front(); q.pop();
int x = t.first, y = t.second;
int tx = x, ty = y + (int)d;//先向右移动d
if((n-x)*(n-x) + (m-y)*(m-y) <= d*d){
dis[n][m] = dis[x][y] + 1;
break;
}
while(tx <= n && ty >= y && ty <= m){
if((tx - x) * (tx - x) + (ty - y) * (ty - y) <= d * d && dis[tx][ty] == 0)
{
q.push(make_pair(tx, ty));
dis[tx][ty] = dis[x][y] + 1;
tx ++;
}
else ty --;
}
}
cout<<dis[n][m]<<endl;
}
int main(){
cin>>n>>m>>d;
bfs();
return 0;
}
ps.
代码部分借鉴自:蓝桥杯校内模拟赛题解