HDU1010 Tempter of the Bone DFS+奇偶剪枝

一道标准的dfs题,并且需要剪枝,否则会TLE。

奇偶剪枝的原理:两点之间的曼哈顿距离为奇数, 则只能在奇数步内到达,偶数同理。即:假设起点为(sx,sy),终点为(ex,ey),给定t步恰好走到终点,则[abs(ex-sx)+abs(ey-sy)]与t同奇偶。

本题用到的另一个剪枝:可走的步数("."的总数+1("D"算一步))< t 时,直接输出“NO”。

此外,我有一个疑问求大佬们解答:当到达终点但时间不对时,直接判false返回还是继续搜索?我认为应该直接return,毕竟一个点只能走一次;但是这样一直WA,所以有没有什么样例可以解释一下?dfs的部分代码如下:

if(maze[xx][yy]=='D')
{
    if(tt==t)
    {
        ans=true;
        return;
    }
    /*else  //走过就不能再走,一定false
        return;*/
}

如果我把注释取消掉,就会报WA,这是为什么呢?请求大佬解答!!

 

另外发现以前一直用的vis数组其实可以不用,只要能标记能回溯即可,代码中也附上了相关内容。AC代码如下:

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

int n,m,t;
char maze[10][10];
bool ans=false;
bool vis[10][10];

int d[4][2]={{-1,0},{0,1},{1,0},{0,-1}};
void dfs(int x,int y,int time)
{
    if(ans==true)
        return;
    if(x<0||x>=n||y<0||y>=m||time>t)
        return;
    for(int i=0;i<4;i++)
    {
        int xx=x+d[i][0],yy=y+d[i][1],tt=time+1;
        if(xx>=0&&xx<n&&yy>=0&&yy<m&&tt<=t)
        {
            if(vis[xx][yy]||maze[xx][yy]=='X')
                continue;
            /*if(maze[xx][yy]=='X')
                continue; //没有vis数组 */
            else if(maze[xx][yy]=='D')
            {
                if(tt==t)
                {
                    ans=true;
                    return;
                }
                /*else  //走过就不能再走,一定false(??)
                    return;*/
            }
            vis[xx][yy]=true;
            dfs(xx,yy,tt);
            vis[xx][yy]=false;

            /* 不用vis也可以标记(已AC)
            char cnt=maze[xx][yy];
            maze[xx][yy]='X';
            dfs(xx,yy,tt);
            maze[xx][yy]=cnt;  */
        }
    }
}
int main()
{
    while(scanf("%d%d%d",&n,&m,&t)==3)
    {
        if(n==0&&m==0&&t==0)
            break;
        memset(maze,'\0',sizeof(maze));
        memset(vis,false,sizeof(vis));
        ans=false;

        int si=0,sj=0,ei=0,ej=0;//起点终点的坐标
        int num=0;//可走的格子总数
        for(int i=0;i<n;i++)
        {
            scanf("%s",maze[i]);
            for(int j=0;j<m;j++)
            {
                if(maze[i][j]=='S')
                {
                    si=i,sj=j;
                    vis[i][j]=true; //注意标记起点!!!
                    /*maze[i][j]='X'; //不用vis数组  */
                }
                if(maze[i][j]=='D')
                    ei=i,ej=j,num++;
                if(maze[i][j]=='.')
                    num++;
            }
        }
        int Manhattan=abs(ei-si)+abs(ej-sj);//起点终点的曼哈顿距离
        if(num<t||(Manhattan%2!=t%2))//剪枝
        {
            printf("NO\n");
            continue;
        }
        dfs(si,sj,0);
        if(ans)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值