题解:ABC272D-Root M Leaper
·题目
链接:Atcoder。
链接:洛谷。
·难度
算法难度:普及。
思维难度:普及。
调码难度:提高
综合评价:中等。
·算法
bfs+数论。
·思路
一、确定那两个点之间有直连边。大家都能想到遍历每一种组合,并判断距离是否为sqrt(m),时间复杂度O(n4)是超时的。但是我们发现我们只需要在遍历第一个点之外仅仅遍历第二个点的横坐标即可,因为纵坐标可以用数学方法确定,范围缩小到0~2个。
二、确定最短路径,直接上bfs。
三、输出答案即可。
总时间复杂度O(n3)。
·细节
一、存储节点时可以用pair<int,int>。
二、数学方法是指通过d2与a2求出b(d2=a2+b2),再由b推出第二个点的纵坐标。
·代码
链接:Atcoder。
链接:洛谷。
#include<bits/stdc++.h>
#define N 440
#define N2 167000
using namespace std;
vector<pair<int,int>>edge[N][N]={};
pair<int,int>q[N2]={};
int dis[N][N]={},m=0,n=0,qfront=0,qrear=0;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
int a2=(k-i)*1LL*(k-i),b=0,b2=0,l1=0,l2=0;
b2=m-a2;
b=sqrt(b2);
if(b*1LL*b==b2){
l1=j-b;
l2=j+b;
if(l1==l2){
edge[i][j].push_back({k,l1});
}else{
if(l1>=1){
edge[i][j].push_back({k,l1});
}
if(l2<=n){
edge[i][j].push_back({k,l2});
}
}
}
}
}
}
memset(dis,-1,sizeof(dis));
qfront=1;
qrear=0;
qrear++;
q[qrear]={1,1};
dis[1][1]=0;
while(qfront<=qrear){
pair<int,int>ot=q[qfront];
qfront++;
for(pair<int,int>i:edge[ot.first][ot.second]){
if(dis[i.first][i.second]==-1){
dis[i.first][i.second]=dis[ot.first][ot.second]+1;
qrear++;
q[qrear]=i;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",dis[i][j]);
}
printf("\n");
}
return 0;
}
·注意
一、数据范围(参见:Atcoder)。