POJ3099Curling 2.0(DFS小变形+模拟)

冰壶游戏弹弹弹
一定要好好读题。。。????
因为没看清这句话,导致一直连样例都过不了

When the stone stands still, you can make it moving by throwing it. You may throw it to any direction unless it is blocked immediately(Fig. 2(a)).

题目大意
冰壶游戏:朝某一方向移动冰壶,求打中目标时需要移动几次。
具体规则如下:
①如果冰壶在移动过程中,下一个位置是墙,会停在原地,下一位置墙壁消失
但是!!!!如果冰壶位置与墙紧挨着,无法向墙的方向移动
③如果冰壶出界,无法击中目标,游戏失败
④如果击中目标的最少次数大于10次,游戏失败

为什么说是DFS小变形呢,因为在冰的部分,不是一块一块地移动了,而是在冰上一直移动,直到出界或撞墙。
为什么说是模拟呢,因为模拟地去想,很容易想到解题方法,也很容易能想到用深搜而不是广搜。

老规矩,先放上DFS代码,然后分步讲解。

void dfs(int x,int y,int num)
{
    ///当前操作
    num++;
    ///结束条件
    if(num>10)
        return;
    ///搜索过程
    for(int i=0;i<4;i++){
        int nx=x+_move[i][0];
        int ny=y+_move[i][1];
        ///请认真阅读题目,谢谢!太菜了,忽略了这一点
        if(maze[nx][ny]==1)
            continue;
        ///一直深入搜索,直到找到一个非0的点
        while(maze[nx][ny]==0){
            nx+=_move[i][0];
            ny+=_move[i][1];
        }
        if(maze[nx][ny]<0)
            continue;
        ///搜索条件
        if(maze[nx][ny]==1){
            maze[nx][ny]=0;
            dfs(nx-_move[i][0],ny-_move[i][1],num);
            ///回溯!
            maze[nx][ny]=1;
        }
        else if(maze[nx][ny]==3){

            ///success=true;
            ///能够达到3这个点并不意味着一定有移动方案,因为还有10次的步数限制
            if(num<minx)
                minx=num;
            continue;
        }

    }
}

DFS四小步:当前操作、结束条件、搜索条件、回溯
前两步不说了,很简单。主要解释一下后两步吧。
题目大意的第二条,我读题时没读到,导致一直错一直错。。。。唉心累
但是!!!!如果冰壶位置与墙紧挨着,无法向墙的方向移动

for(int i=0;i<4;i++){
        int nx=x+_move[i][0];
        int ny=y+_move[i][1];
        ///请认真阅读题目,谢谢!太菜了,忽略了这一点
        if(maze[nx][ny]==1)
            continue;

四个方向搜索,无先后顺序,如果下一个位置是墙的话,continue,不进行后续操作~~
然后是一直深入搜索,直到找到一个非0的点,这里要说明一下,其实在主函数里边,我把出发点的2变成了0,就是为了这个地方方便一些,我朝一个方向移动但凡没有撞到墙出界成功击中,我就继续走

        while(maze[nx][ny]==0){
            nx+=_move[i][0];
            ny+=_move[i][1];
        }

直到①出界②撞墙③成功击中

		if(maze[nx][ny]<0)
        else if(maze[nx][ny]==1)
        else if(maze[nx][ny]==3)

①出界用了一个小技巧,在主函数中初始化的时候把整个地图先赋值为-1,再输入0123,这样判断是否出界只需要判断是不是小于0就OK了。
②撞墙的话,要注意:
一方面现在的nx,ny是移动以后的,因为撞墙要在原地不动,所以要回退一下,很容易错。
另一方面要注意到本题是需要回溯的。因为不同的走法可能会走相同的地方,之前砸烂的墙还需要恢复原状。

		else if(maze[nx][ny]==1){
            maze[nx][ny]=0;
            dfs(nx-_move[i][0],ny-_move[i][1],num);
            ///回溯!
            maze[nx][ny]=1;
     	}

③成功击中的话,也有个小地方需要注意,我最开始写的时候,定义了一个bool success,当进入这个if语句后我认为能找到出口,标记success为false,在主函数输出的时候,如果success==false,输出-1,否则输出minx。
但是,这其实是有漏洞的!因为即使你进入了该if语句,能够击中目标,但是是否满足步数小于10的条件还不一定。
比如说,只有一条路可以击中目标,但是需要100步,所以正确答案应该是-1,但是由于进入了该if语句,你的success已经认为可以找到路径了,会输出100,所以会WA。

		else if(maze[nx][ny]==3){
            ///success=true;
            ///能够达到3这个点并不意味着一定有移动方案,因为还有10次的步数限制
            if(num<minx)
                minx=num;
            continue;
        }

反正就感觉,做一道题,需要注意的细节,需要考虑的方面太多了,也是在磨砺中成长吧!加油!!!

最后给出AC代码。

#include<stdio.h>
#include<string.h>
#define maxn 25
#define inf 0x7fffffff
int maze[maxn][maxn];
int minx,w,h,sx,sy;
int _move[4][2]={{1,0},{-1,0},{0,-1},{0,1}};
void dfs(int x,int y,int num)
{
    ///当前操作
    num++;
    ///结束条件
    if(num>10)
        return;
    ///搜索过程
    for(int i=0;i<4;i++){
        int nx=x+_move[i][0];
        int ny=y+_move[i][1];
        ///请认真阅读题目,谢谢!太菜了,忽略了这一点
        if(maze[nx][ny]==1)
            continue;
        ///一直深入搜索,直到找到一个非0的点
        while(maze[nx][ny]==0){
            nx+=_move[i][0];
            ny+=_move[i][1];
        }
        if(maze[nx][ny]<0)
            continue;
        ///搜索条件
        else if(maze[nx][ny]==1){
            maze[nx][ny]=0;
            dfs(nx-_move[i][0],ny-_move[i][1],num);
            ///回溯!
            maze[nx][ny]=1;
        }
        else if(maze[nx][ny]==3){
            ///success=true;
            ///能够达到3这个点并不意味着一定有移动方案,因为还有10次的步数限制
            if(num<minx)
                minx=num;
            continue;
        }

    }
}
int main()
{
    while(~scanf("%d%d",&w,&h))
    {
        minx=inf;
        if(w==0&&h==0)
            break;
        memset(maze,-1,sizeof(maze));
        for(int i=0;i<h;i++){
            for(int j=0;j<w;j++){
                scanf("%d",&maze[i][j]);
                if(maze[i][j]==2){
                    sx=i;
                    sy=j;
                }
            }
        }
        maze[sx][sy]=0;
        dfs(sx,sy,0);
        if(minx<inf)
            printf("%d\n",minx);
        else
            printf("-1\n");
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值