bfs类的应用题。
解法:
每一个点都可能作为汇集的那个点,因此采用遍历的方式,对每个点进行处理,得出每个点的“所有马跳到本点的最小步数和“,取最小值即可。
逻辑1:以该点作为源点出发,求处从该点出发访问所有马(如果能访问完)所需的最小步数。根据马走日的规则,下一步有八个格子可作为”下一个格子“,”下一个格子“可能是马,也有可能是空格。
如果是马,该马有自己的可跳距离k,从该马的位置离源点的步数step(暗示用队列)已知,若k>=step,说明该马可跳到汇集点(源点),否则结束,因为有一个马无法跳到汇集点;
如果是空格,该空格可能作为马到汇集点的一个中转点。
逻辑2:不管下一个格是马还是空格,从该位置到汇集点的最小步数是动态更新的(同亲子游戏,或从示例演算得出),因此需要为该位置维护一个动态值,使用二维数组完成。此外,对于每个点,其下一步所到达的格子是确定的(按规则走,最多8格),所以每个点至多加入队列一次,重复加入无意义(因为该点到源点的最小步数是动态维护的)且会爆内存。
代码
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner in=new Scanner(System.in);
String[] size=in.nextLine().split(" ");
int m=Integer.parseInt(size[0]),n=Integer.parseInt(size[1]);
char [][] board=new char[m][n];
// 马的个数
int count=0;
for(int i=0;i<m;i++){
String s=in.nextLine();
for(int j=0;j<n;j++){
board[i][j]=s.charAt(j);
if(board[i][j]!='.'){
count++;
}
}
}
//以每一个“马”出发,在所有马限定的步数board[i][j]下,感染完所有马所需的最小步数h,取h的最小值
// bfs搜索
int[][] arr=new int[][]{{1,2},{1,-2},{2,1},{2,-1},{-1,2},{-1,-2},{-2,1},{-2,-1}};
int ans=Integer.MAX_VALUE;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
//record[i][j]:从点(i,j)处到源点(i0,j0)处所需的最小步数
int[][] record=new int[m][n],used=new int[m][n];
Deque<int[]> queue=new ArrayDeque<>();
queue.add(new int[]{i,j});
// cnt:可跳到源点处的马的数量
int cnt=0;
while(!queue.isEmpty()){
int[] cur=queue.poll();
int row=cur[0],col=cur[1],step=record[row][col];
//防止回访
used[row][col]=1;
// 下一跳检查
for(int[] a:arr){
int nr=row+a[0],nc=col+a[1];
if(nr<0||nr>=m||nc<0||nc>=n||used[nr][nc]==1){
continue;
}
if((board[nr][nc]!='.'&&(board[nr][nc]-'0')>=step+1)||(board[nr][nc]=='.')){
if(record[nr][nc]==0){
if(board[nr][nc]!='.'){
cnt++;
}
//step+1是最小值
record[nr][nc]=step+1;
//继续搜索
queue.add(new int[]{nr,nc});
}else{
//更新值即可
record[nr][nc]=Math.min(record[nr][nc],step+1);
}
}
}
}
//维护最小步数和ans
int sum=0,copy=count-1;
if(board[i][j]=='.'){
copy=count;
}
if(cnt==copy){
for(int r=0;r<m;r++){
for(int c=0;c<n;c++){
if(board[r][c]!='.'){
sum+=record[r][c];
}
}
}
ans=Math.min(ans,sum);
}
}
}
System.out.println(ans==Integer.MAX_VALUE?-1:ans);
}
}