算法-跳马

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);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暗=里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值