杭电ACM OJ 1010 Tempter of the Bone 图的深度搜索,奇偶剪枝,难得有一道像点样子的好题

Tempter of the Bone

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 131410    Accepted Submission(s): 35386


Problem Description
The doggie found a bone in an ancient maze, which fascinated him a lot. However, when he picked it up, the maze began to shake, and the doggie could feel the ground sinking. He realized that the bone was a trap, and he tried desperately to get out of this maze.

The maze was a rectangle with sizes N by M. There was a door in the maze. At the beginning, the door was closed and it would open at the T-th second for a short period of time (less than 1 second). Therefore the doggie had to arrive at the door on exactly the T-th second. In every second, he could move one block to one of the upper, lower, left and right neighboring blocks. Once he entered a block, the ground of this block would start to sink and disappear in the next second. He could not stay at one block for more than one second, nor could he move into a visited block. Can the poor doggie survive? Please help him.
 

Input
The input consists of multiple test cases. The first line of each test case contains three integers N, M, and T (1 < N, M < 7; 0 < T < 50), which denote the sizes of the maze and the time at which the door will open, respectively. The next N lines give the maze layout, with each line containing M characters. A character is one of the following:

'X': a block of wall, which the doggie cannot enter; 
'S': the start point of the doggie; 
'D': the Door; or
'.': an empty block.

The input is terminated with three 0's. This test case is not to be processed.
 

Output
For each test case, print in one line "YES" if the doggie can survive, or "NO" otherwise.
 

Sample Input
  
  
4 4 5 S.X. ..X. ..XD .... 3 4 5 S.X. ..X. ...D 0 0 0
 

Sample Output
  
  
NO

YES

翻译:小狗闯迷宫,比如这种形式的

S.X.

..X.

..XD

....

S是起点,D是终点,X是墙壁,即不可抵达的区域,规定一个步数,比如题中给出的5,就是要求必须在刚好5步的时候到达,早一步到达不行,晚一步也不行,而且走过的路不能再走一次。4 4 5,4就是行数,第二个4就是列数,5就是要求的步数

下面代码的顶部会给出非常精彩的解答,一看就懂的那种

public class TempterOfTheBone1010 {

    /**
     * 解决本题,只需要明白:图的深度搜索 这个知识点了
     *(因为终点,早到也不行,晚到也不行,所以必须用深度搜索求出每种情况)
     * 这样就很简单了,1.找出所有可能的情况2.匹配这个步数 即可
     *
     * 深度搜索怎么弄?就是从起点开始递归,有4个方向可以走,在每个方向都重新调用一次递归,
     * 直到遇到终点且同时记录的步数和要求的步数是一致的,其中需要记录的东西有 flag,成功与否的
     * 标志, 另一个二维数组visitState【】【】来判断走过没有,走过记录为1.
     *
     * 怎么理解这个递归:
     * for (int i = 0; i < 4; i++) 就这样一个for循环,分别代表上下左右4个方向,进行4次递归就可以,
     * 分别是从起始点的上下左右4个相邻区域进行的递归。因为采用的是递归,你可以认为上下左右返回给你的
     * 4个递归已经是实现了所有情况的递归了。再考虑起始点的任意相邻点,也这样考虑即可,返回给你的已经
     * 是实现了所有情况的递归了。想理解深度搜索,主要是理解递归。
     *
     * 如果想优化,想加快效率,就要用到 奇偶剪枝的思想 可以查看网站
     * http://blog.csdn.net/nvliba/article/details/48532709
     * 非常的简短有力,1分钟即可看懂
     * 其主要目的是排除掉某些不可能的情况,加快效率
     * */

    private static String[][] strings =
            {{"S", ".", "X", "."},
             {".", ".", "X", "."},
             {".", ".", "X", "D"},
             {".", ".", ".", "."}};

    private static int mSX;//起始
    private static int mSY;
    private static int mDX;//终点门
    private static int mDY;

    private static int width = strings[0].length;
    private static int height = strings.length;

    private static int required = 7;//需求步数

    private static int[][] visitState = new int[height][width];//受访状态
    private static int flag = 0;//是否成功标志

    public static void main(final String[] args) throws Exception {

        initData();

        boolean result = calculate();

        System.out.println(result + "");
    }

    private static void initData() {
        //定位起始点和终点
        int sX = 0;
        int sY = 0;
        int dX = 0;
        int dY = 0;
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                if (strings[i][j].equals("S")) {
                    sX = i;
                    sY = j;
                }
                if (strings[i][j].equals("D")) {
                    dX = i;
                    dY = j;
                }
            }
        }
        mSX = sX;
        mSY = sY;
        mDX = dX;
        mDY = dY;

        //设置起始点已经访问过,之后每走过一次,都置为1
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                visitState[i][j] = 0;
            }
        }
        visitState[sX][sY] = 1;
    }

    //这里是判断这个点是否可走的方法
    private static boolean ifAccess(int x, int y) {
        if (x < 0 || x > width - 1 || y < 0 || y > height - 1 || strings[x][y].equals("X")
                || visitState[x][y] == 1) return false;
        return true;
    }

    private static void DFS(int x, int y, int step) {//step为第几步
        //递归的时候,如果发现了Door,且刚好和要求的步数一致,flag置为1,成功;步数不一致,失败
        if (strings[x][y].equals("D")) {
            if (step == required) {
                flag = 1;
                return;
            }
            return;
        }

        //每次都要计算还剩下多少最短路径(无视障碍X),来判断奇偶剪枝
        int minSteps = Math.abs(x - mDX) + Math.abs(y - mDY);
        if ((required - minSteps - step) % 2 != 0 || minSteps + step > required) return;

        System.out.println(minSteps + "");

        int nextPossibleX = 0;
        int nextPossibleY = 0;
        for (int i = 0; i < 4; i++) {
            switch (i) {
                case 0:
                    nextPossibleX = x;
                    nextPossibleY = y - 1;//                    break;
                case 1:
                    nextPossibleX = x;
                    nextPossibleY = y + 1;//                    break;
                case 2:
                    nextPossibleX = x - 1;//                    nextPossibleY = y;
                    break;
                case 3:
                    nextPossibleX = x + 1;//                    nextPossibleY = y;
                    break;
            }
            if (ifAccess(nextPossibleX, nextPossibleY)) {

                visitState[x][y] = 1;
                //这样递归 相当于把这个方向的出去都走了一遍,循环4次,等于走遍全图
                DFS(nextPossibleX, nextPossibleY, ++step);
                //如果一路上都没有return 就相当于没找到flag =1,即不成功,
                // 退一格,受访状态重新清零,继续换另外方向找
                // (即:从上开始找,step+1,横纵坐标赋值赋好,上面一格置为已经访问,上找不到
                // 再把3个数据:step,访问状态,横纵坐标恢复原值,继续换个方向找,即下,左,右)
                visitState[nextPossibleX][nextPossibleY] = 0;
                step--;
            }
        }
    }

    private static boolean calculate() {
        int minSteps = Math.abs(mSX - mDX) + Math.abs(mSY - mDY);
        if (minSteps > required) return false;
        DFS(mSX, mSY, 0);
        if (flag == 1) {
            return true;
        } else {
            return false;
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值