hdu 1010 Tempter of the Bone dfs

传送门http://acm.hdu.edu.cn/showproblem.php?pid=1010

收获最大的一个dfs了,写了一天,最后看到了一个让人无语的错误,反省ing'~~

dfs的多重递归确实比较伤,很难debug,我是直接在dfs的开始输出了x,y这种方法不错,推荐一下!!!


首先是最重要的剪枝问题,我在这道题用到了两种。首先是最基础的,从实用性来讲最高的,奇偶剪枝——

原理大致就是先把图建成0,1间隔的样子,比如

0101010101

1010101010

0101010101

1010101010

显然从0到1,从1到0需要奇数步,而从0到0,从1到1则是固定为偶数步,所以可以凭此剪枝。

之后是这个能用的一个路径剪枝,这个题有个最大的问题,没有完善数据范围,所以可以通行的块数小于走的步数的数据是合法的。所以可以统计墙的个数来做比较,去除这种行为。

最后是网上看到的一种动态剪枝,简直imba,貌似叫等高线剪枝法。就这个图为例

 
 
   
   
S.X. ..X. ..XD ....
上来先构建他的每个可行点到D的最短路,不需要什么算法,只要把D设为0,上下左右的可行点+1以此类推,可以得到下图

 
 
S6X2 65X1 54XD 4321
每当更新到一个点时,将剩余步数和最小步数比较,如果剩余步数小于最小步数,那么就可以去除了。

最后还要检讨下自己,刷水题的日子整天dnf,巫师3,浪费了好时光,导致代码优化跟屎一样,不但长,bug层出不穷,以后还是要多敲啊,另求debug正确姿势,有心得的朋友不妨交流一下。

下面上代码,这个注释比较多,

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<sstream>
#include<stack>
#include<queue>
#include<fstream>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<functional>
#include<cmath>
using namespace std;
#define maxn 10
int n,m,t;
int sx,sy,dx,dy,flag,xnum;
bool jiou1[maxn][maxn];
char k;
int map1[maxn][maxn];
int dirx[4]= {-1,0,1,0}; //方向
int diry[4]= {0,-1,0,1};
void jiou(int sx,int sy,int dx,int dy,int t,int m,int n)
{
    flag=0;
    for(int z=0; z<n; z++)
    {
        for(int a=0; a<m; a++)
        {
            if(a%2==z%2)
            {
                jiou1[a][z]=0;
            }
            else
                jiou1[a][z]=1;
        }
    }
    if(jiou1[sx][sy]==jiou1[dx][dy])
    {

        if(t%2==1)
        {
            flag=1;
            cout<<"NO"<<endl;
        }
    }
    else if(jiou1[sx][sy]!=jiou1[dx][dy])
    {
        if(t%2==0)
        {
            flag=1;
            cout<<"NO"<<endl;
        }
    }
}//flag==1结束
void dfs(int x,int y,int t)
{
    int mx,my;
    if(flag==1)
        return;
    if(x==dx&&y==dy)
    {
        if(t==0)
            flag=1;
        return;
    }
    if(t<=0)
    {
        return;
    }
    for(int l=0; l<4; l++)
    {
        mx=x+dirx[l];
        my=y+diry[l];
        if(map1[mx][my]==1&&mx>=0&&mx<=m-1&&my>=0&&my<=n-1)
        {
            map1[mx][my]=0;
            dfs(mx,my,t-1);
            map1[mx][my]=1;//使用过的点这里记得变回去
        }
    }

}//1为可通行,0为墙或者走过的,flag为1表示可到达
void jianzhi(int n,int m,int xnum,int t)
{
    flag=0;
    if(n*m-xnum-1<t)
    {
        flag=1;
        cout<<"NO"<<endl;
    }
}//flag==1结束
int main()//一切数组计数从0开始,坐标计数从0开始
{
    while(cin>>n>>m>>t&&(n+m+t)!=0)//n是纵长,m是横长,图从左上角开始构造
    {
        xnum=0;
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<m; j++)
            {
                cin>>k;
                if(k=='S')
                {
                    sx=j;
                    sy=i;
                }
                if(k=='D')
                {
                    dx=j;
                    dy=i;
                }
                if(k=='X')
                {
                    map1[j][i]=0;
                    xnum++;
                }
                else
                {
                    map1[j][i]=1;

                }
            }
        }
        map1[sx][sy]=0;//这里是因为会出现这种情况比如00是S,01是.那么00会搜01,而00的值不是0,
                       //所以会搜回去,但是再搜回来会发现01不能通过了以至于得到错误的答案
        //完成赋值,可用为1,墙为0;
        jiou(sx,sy,dx,dy,t,m,n);
        if(flag==1)
        {
            continue;
        }
        jianzhi(n,m,xnum,t);
        if(flag==1)
        {
            continue;
        }
        flag=0;
        dfs(sx,sy,t);
        if(flag==1)
            cout<<"YES"<<endl;
        else
            cout<<"NO"<<endl;
    }
    return 0;
}
写了这么多再看看大神的57行,流泪到天亮啊~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值