1. 程式人生 > >HDU 1010 Temper of the Bone

HDU 1010 Temper of the Bone

一、題目描述

      幫助doggie走出迷宮。迷宮由‘S’(起點)、‘X’(牆,不可通過)、‘D’(終點)、和‘.’(可走格子)組成。給定時間T,doggie每秒走一格,要求不可重複走,且剛好在T秒內走到終點D。如果有這樣的路徑,輸出“YES”,否則“NO”。

二、樣例

輸入:


  
   4 4 5 S.X. ..X. ..XD .... 3 4 5 S.X. ..X. ...D 0 0 0
   
  輸出: 
 

NO
YES

三、分析

     深搜DFS問題。從起點開始,doggie每秒有4種走法,即上、下、左、右,下一秒又在上一次的基礎上有4種選擇。我們要做的就是對這所有的情況剪枝。

1、邊界。遇到迷宮的邊緣,或者代表無法通過的X,不再繼續搜尋;

2、重複。已經走過的區域不再重複搜尋;

3、奇偶剪枝。這個不太容易想到。可以這樣考慮:先計算起點到終點的最短距離d,由於有牆的存在,給的時間走過的步數T減去最短距離d,也就是需要繞路走的步數 t = T - d。這個繞路步數必須是偶數,否則無法到達終點。

4、時間。如果給的時間超過可走的步數+1,則一定無法到達。具體可以看這個樣例:

   2 4 7
   S..D
   ....

    可走塊數sum為6,時間T為7,剛好走完所有塊到達終點。若sum < T - 1,一定無法到達。   

    另外要注意的就是遞迴,要小心跳出條件和恢復上一層。

四、程式碼

   
/**********************************************
*時間:2018-01-29
*內容:深搜+剪枝
*技巧:搜尋的時候剪枝一定要考慮完全,能剪的都
       剪掉,否則容易超時;遞迴思路要清晰
***********************************************/
#include<iostream>
#include<cstring>
#include<cmath>
#include <stdlib.h>
using namespace std;

int dx[4]={1,0,-1,0},dy[4]={0,-1,0,1};
int n,m,t,sum;   //記錄迷宮大小和時間,可走的塊數
int sx,sy,ex,ey; //記錄起點和終點座標
char maze[7][7];
bool record[7][7];//記錄迷宮是否被走過
bool flag = false;

void dfsMaze(int nx,int ny,int layer)
{
    if(layer == t ){ //跳出條件
        if(maze[nx][ny] == 'D' ) flag = true;
        return;
    }
    for(int i = 0; i < 4; ++ i){
        //排除邊界情況
        if(nx + dx[i] < 0 || nx + dx[i] == n || ny + dy[i] < 0 || ny + dy[i] == m) continue;
        //對牆 和 已經走過的部分 剪枝
        if(maze[nx + dx[i]][ny + dy[i]] == 'X' || !record[nx + dx[i]][ny + dy[i]]) continue;
        //如果在時間到之前就走到D,也無法走出迷宮。剪枝
       if(layer < t - 1 && maze[nx + dx[i]][ny + dy[i]] == 'D') continue;
        record[nx+dx[i]][ny+dy[i]] = false; //記錄該塊已經走過
        nx = nx + dx[i];
        ny = ny + dy[i];

        //進入下一層搜尋
        dfsMaze(nx,ny,++ layer);
        //恢復
        if(flag) return; //只要找到一條路線,就不再尋找
        record[nx][ny] = true;
        layer --;
        nx = nx - dx[i];
        ny = ny - dy[i];

    }
}
int main()
{
    while(true){//讀入資料
        cin>>n>>m>>t;
        if(n==0) break;
        memset(record,true,sizeof(record));
        flag = false;
        sum = 0;
        for(int i = 0;i < n; ++ i){
            for(int j = 0;j < m; ++ j){
                cin >> maze[i][j];
                if(maze[i][j] == '.') sum++;
                //記錄終點和起點位置
                if(maze[i][j] == 'D'){ex = i;ey = j;}
                if(maze[i][j] == 'S'){sx = i;sy = j;record[i][j] = false;}
            }
        }
        //如果可走的步數小於時間-1,或者需要繞彎走的步數是奇數,不可到達
        if(sum < t - 1 || (t - (abs(sx - ex) + abs(sy - ey))) % 2)cout<< "NO" <<endl;
        else{
            dfsMaze(sx,sy,0);
            if(flag) cout << "YES" << endl;
            else cout << "NO" << endl;
        }
    }
}