HDU1010 Tempter of the Bone(回溯 + 剪枝)

本文链接:http://i.cnblogs.com/EditPosts.aspx?postid=5398734

题意:

  输入一个 N * M的迷宫,这个迷宫里'S'代表小狗的位置,'X'代表陷阱,‘D’代表门,‘.’代表可行走的地方,小狗每次可以选择往周围的四个方向行走,问这个小狗能否正好T步找到门。

思路:

  利用回溯 + 剪枝,这道题剪枝特别重要。

剪枝一:

可以把图看成这样:

1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1

则假设从点 a(i + j,横纵坐标之和) 走到点 b(i + j) ,如果 a 和 b 的奇偶性相同,那么从 a 到 b 必须是偶数步.如果 a  和 b 的奇偶性不同则走过的步数必须是奇数步。所以 当 (a + b + T)为奇数时一定不能恰好到达。

剪枝二:

如果已经找到答案就没有要继续求解。

剪枝三:

T大于从S到D的最长路径,小于从S到D的最短路径,则无解。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
using namespace std;

const int MAXN = 11;
char Gra[MAXN][MAXN];
int stepX[] = {0, 0, 1, -1};
int stepY[] = {-1, 1, 0, 0};
int N, M, T;
int cnt;
int beginX, beginY, endX, endY;
int OK;

int check(int x, int y)
{
    if(Gra[x][y] == 'O')  return 0;   // 走到了已经走过的路
    if(Gra[x][y] == '*')  return 0;   //走出了边界
    if(Gra[x][y] == 'X')  return 0;    //走到了墙
    if(cnt > T)           return 0;  //在此时走的步数已经大于总步数则
                          return 1;
}

void backtrack(int x, int y)
{
    if(x == endX && y == endY ) //到达终点
    {
       if(cnt == T)          //找到了答案
           OK = 1;
    }
    else
    {
        for(int i = 0; i < 4; i++)  //在这点一共有四种选择(状态)
        {
            int tx =  x + stepX[i];
            int ty =  y + stepY[i];
            if(check(tx, ty))    //检查所选择的状态是否合理
            {
                ++cnt;              //记录走过的步数
                Gra[tx][ty] = 'O';   //标记走过的地方
                if(OK) return;    //尤为重要的剪枝 ,如果找到答案,就不用继续递归,
                backtrack(tx, ty);
                Gra[tx][ty] = '.';  //恢复现场
                --cnt;

            }
        }
    }
}

int main()
{
    //freopen("in.txt","r", stdin);
    while(~scanf("%d%d%d",&N, &M, &T) && N)
    {
        getchar();
        beginX = beginY =  endX = endY = 0;
        memset(Gra, '*', sizeof(Gra));
        for(int i = 1; i <= N; i++)
        {
            for(int j = 1; j <= M; j++)
            {
                scanf("%c",&Gra[i][j]);
                if(Gra[i][j] == 'S')
                    beginX = i, beginY = j;
                if(Gra[i][j] == 'D')
                    endX = i, endY = j;
            }
            getchar();
        }
        OK = 0;
        if( (T > M * N) || ( T < (abs(beginX - endX) + abs(beginY - endY)) ) || ( (beginX + beginY + endX + endY + T) & 1 ))//剪枝二、三
        {
            printf("NO
"); continue;
        }
        cnt = 0;
        Gra[beginX][beginY] = 'O';
        backtrack(beginX, beginY);
        if(OK) printf("YES
");
        else printf("NO
");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Ash-ly/p/5398734.html