Find a way——HDU 2612

题目

传送门
Pass a year learning in Hangzhou, yifenfei arrival hometown Ningbo at finally. Leave Ningbo one year, yifenfei have many people to meet. Especially a good friend Merceki.
Yifenfei’s home is at the countryside, but Merceki’s home is in the center of city. So yifenfei made arrangements with Merceki to meet at a KFC. There are many KFC in Ningbo, they want to choose one that let the total time to it be most smallest.
Now give you a Ningbo map, Both yifenfei and Merceki can move up, down ,left, right to the adjacent road by cost 11 minutes.

迈克和易芬飞要见面,他们要在肯德基见面,问你他们选择在哪一家肯德基见面可以使得总时间最短。他们每个人往上下左右走一步,所花时间为11分钟!

Input

The input contains multiple test cases.
Each test case include, first two integers n, m. (2<=n,m<=200).
Next n lines, each line included m character.
‘Y’ express yifenfei initial position.
‘M’ express Merceki initial position.
‘#’ forbid road;
‘.’ Road.
‘@’ KCF
翻译: Y是易芬飞的出发位置,M是迈克的位置,‘#’是不可以走过的,‘.’是可以走路的,‘@’是肯德基所在的位置。
输入一个数和一个地图,问你最小的时间?

Output

For each test case output the minimum total time that both yifenfei and Merceki to arrival one of KFC.You may sure there is always have a KFC that can let them meet.
输出最小的时间

思路

因为我们知道,利用广搜,只要搜到肯德基,那么肯定是最优的走法。【因为广搜是的步数是从下往上递增的,所以,只要第一次出现了,我们就可以判断为这个时候走到该肯德基是最优的】

我们可以利用两个人分别进行广搜,搜到的肯德基节点就存在自己对应的可达队列中,这样到时候,利用两个人的可达队列去进行比较,只要坐标一致,那么就证明他们到达某个肯德基都是最优的,这个时候将两人步数加起来,然后最小步数去两者之中小的。

两人可能有一些节点是到达不了的,比如下面这个例子

4 5
@.#@.
…Y#.
@#M#.
@…#@

所以,我们不能用肯德基的个数来判断最终他们每个人的可达肯德基的数目。这个是本道题踩得第一个坑!!!切记
两人所能够到达的节点数目是一样的吗? 答案是一定是一样的。题目说了,两个人中间一定有可达的肯德基。

You may sure there is always have a KFC that can let them meet.

表明两个人之间的路线一定是联通的,也就是,两个人一定可以通过某个路径到达对方所在的位置,那么这样子,只要一个人能去到的肯德基,另外一个人也一定可以去到!


我们先利用一个循环,将整个地图收录进来,在这过程中,我们可以顺便找出M的坐标和Y的坐标。并且在这个过程中,初始化标记数组。【因为我们需要进行两次广搜,所以需要初始化标记数组两次】

进行第一次广搜,搜一个人的可达肯德基数目,只要能到达,就把改点加入到自己的可达肯德基队列中,继续往下搜。【这个地方,当我们搜到一间肯德基的时候,我们不能直接弹出该节点,我们只能放到可达肯德基队列中,并且还要继续广搜,因为有可能那一间最优的肯德基就在当前肯德基的隔壁,我们需要通过这间肯德基来走到最优的位置,这里又是这道题的一个坑点】

再次初始化标记数组,然后按照上述步骤,继续广搜第二个人的对应的可达肯德基队列。

最后,每次取出相同的肯德基,步数之和加起来,不断取最小值,在输出的时候,我们输出最小值乘以11即可。

注意检查可扩展条件,要将对方的位置加进去,例如下面这种情况!,最优的是左上角,其中有人需要越过对方的位置去到一家肯德基。

4 5
@YM…
…#.
@#.#.
@…#@

代码

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

class Point{
    int x;      //行坐标
    int y;      //纵坐标
    int step;   //总步数
}

public class  Main{

    static int visited[][]=new int[210][210];//标记数组
    static char map[][]=new char[210][210];//地图
    static int n; //行数
    static int m;//列数

    //第一个人的横纵坐标
    static int Y_x;
    static int Y_y;
    //第二个人的横纵坐标
    static int M_x;
    static int M_y;
    static Queue<Point> queue=new LinkedList();//搜索用到的队列
    static Queue<Point> Y_queue=new LinkedList();//Y的可达肯德基队列
    static Queue<Point> M_queue=new LinkedList();//M的可达肯德基队列

    //右下左上
    static int d_x[]={0,1,0,-1};
    static int d_y[]={1,0,-1,0};

    public static void main(String[] args) {
        Scanner reader=new Scanner(System.in);
        while (reader.hasNextInt())//多组输入
        {
            n=reader.nextInt();
            m=reader.nextInt();
            String blank=reader.nextLine();//接收回车符

            //接收整个地图并且标记处两个人的坐标和肯德基的个数
            for (int i=0;i<n;++i)
            {
                String test=reader.nextLine();//接收字符串
                for (int j=0;j<m;++j)
                {
                    map[i][j]=test.charAt(j);
                    if (map[i][j]=='Y')//第一个人的坐标
                    {
                        Y_x=i;
                        Y_y=j;
                    }
                    if (map[i][j]=='M') //第二个人的坐标
                    {
                        M_x=i;
                        M_y=j;
                    }
                    visited[i][j]=0;//第一次初始化标记数组
                }
            }


            //找出Y到可达肯德基的每一个最短路径
            queue.clear();
            Y_queue.clear();

            Point temp_Y=new Point();
            temp_Y.x=Y_x;
            temp_Y.y=Y_y;
            temp_Y.step=0;
            queue.offer(temp_Y);

            while (!queue.isEmpty())
            {
                int temp_a=queue.peek().x;
                int temp_b=queue.peek().y;
                if (map[temp_a][temp_b]=='@')
                {
                    Y_queue.offer(queue.peek());//把对应的点加入到可达肯德基队列
                }

                for (int i=0;i<=3;++i)
                {
                    int temporary_x=temp_a+d_x[i];
                    int temporary_y=temp_b+d_y[i];
                    if (check(temporary_x,temporary_y))
                    {
                        Point g=new Point();
                        g.x=temporary_x;
                        g.y=temporary_y;
                        g.step=queue.peek().step+1;
                        queue.offer(g);
                        visited[temporary_x][temporary_y]=1;
                    }
                }
                queue.poll();
            }


            //再次初始化整个标记数组
            for (int i=0;i<n;++i)
                for (int j=0;j<m;++j)
                    visited[i][j]=0;
            //找出M到每一个可达肯德基的最短路径
            queue.clear();
            M_queue.clear();
            Point temp_M=new Point();
            temp_M.x=M_x;
            temp_M.y=M_y;
            temp_M.step=0;
            queue.offer(temp_M);

            while (!queue.isEmpty())
            {
                int temp_c=queue.peek().x;
                int temp_d=queue.peek().y;
                if (map[temp_c][temp_d]=='@')
                {
                    M_queue.offer(queue.peek());//把对应的点加入到可达肯德基队列
                }

                for (int i=0;i<=3;++i)
                {
                    int temporary_x=temp_c+d_x[i];
                    int temporary_y=temp_d+d_y[i];
                    if (check(temporary_x,temporary_y))
                    {
                        Point g=new Point();
                        g.x=temporary_x;
                        g.y=temporary_y;
                        g.step=queue.peek().step+1;
                        queue.offer(g);
                        visited[temporary_x][temporary_y]=1;
                    }
                }
                queue.poll();
            }


            int min=Integer.MAX_VALUE;//两人到某个肯德基之和的最短路径

            //两个队列的长度一定是一致的,没必要两个都判断
            while (!M_queue.isEmpty())
            {
                if(M_queue.peek().x==Y_queue.peek().x && M_queue.peek().y==Y_queue.peek().y)
                {
                    int k=M_queue.poll().step+Y_queue.poll().step;
                    if (min>k)
                        min=k;
                }
                else
                {
                    Y_queue.offer(Y_queue.poll());//弹出后插入到队尾
                }
            }
            System.out.println(min*11);
        }
    }

    //检查一个点是否可以扩展,中间的@。肯德基一定可以扩展,还有一些是要通过对方的位置才能达到最优肯德基,所以对方的位置也可以加进去
    public static boolean check(int w,int h)
    {
        return w>=0 && w<n && h>=0 && h<m && (map[w][h]=='.' || map[w][h]=='@'|| map[w][h]=='Y' || map[w][h]=='M') && visited[w][h]==0;
    }
}

结果

在这里插入图片描述
虽然一次ac,但是是上面那个有可能两个人都有不能到达的例子给我提了个醒,后面才思考了这么多。关于踩得坑点,上面都写了,不再赘述。

无穷尽也!冲

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值