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);}}}}//维护最小步数和ansint 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);}
}