Curling 2.0 DFS+回溯+剪枝

http://poj.org/problem?id=3009

大致题意:一只冰壶在n*m的格子上运动,0代表空地,1代表石头,2代表起点(可走),3代表终点(不可走).


冰壶在运动过程中,每次只朝一个方向移动直到遇到石头1或终点3。若遇到3,到达终点不再移动;若遇到1,则停在当前位置(注意并不替代石头位置),石头位置由1变为0,然后冰壶改变方向继续移动。


还有,若冰壶一直走到边界也没遇到石头或终点,那么这条路是不可行的。

问冰壶从2到3的最小步数,步数的计算方法是冰壶运动过程中方向的改变次数。


注意的是:

冰壶撞到石头后,冰壶会停在石头前面,此时(静止状态)才允许改变冰壶的运动方向,而该块石头会破裂,石头所在的区域由1变为0. 也就是说,冰壶撞到石头后,并不会取代石头的位置。

终点是一个摩擦力很大的区域,冰壶若到达终点3,就会停止在终点的位置不再移动。

 

解题思路:

要先明确:

0为滑动区域

1为石头区域

2为起点,也是可滑动区域

3为终点,不可滑动区域

(1) 起点为“2”,也是一个可滑动的区域,所以标记起点位置之后,可以把起点当做0

(2) 注意区分冰壶是运动的还是静止的,若是静止的话,旁边1格有石头是不能走的。

(3) 输出冰壶从2到3的最短路,如果最短路的步数大于10(不包括10),视作无法走到终点(其实这是用来剪枝的)

(4) 滑动过程中冰壶不允许出界

 基于上面的原则,不难发现:

(1)所谓的“走一步”,就是指冰壶从一个静止状态到下一个静止状态,就是说冰壶在运动时经过的“格数”不视作“步数”,也就是说冰壶每次移动的距离都是不定的。

(2)还有就是由于石头会因为冰壶的碰撞而消失,因此冰壶每“走一步”,场地的环境就会改变一次。

(3)基于(2),可以发现本题虽然是要找 “最短路”,但是BFS几乎不可能,因为每“走一步”,场地的状态就要改变一次;而如果该步不满足要求,又要求把场地的状态还原到前一步,这只有DFS能做到。

(4)基于(3),DFS不是BFS,不能简单地用它来找最短路,必须要把所有可能的路一一找出来,再逐一比较它们的步数才能确定最短。但题目值允许1000MS,此时就面临一个超时的问题。所以题目才同时给出“步数超过10则视为失败”的条件,这是用来剪枝

 

有了上面的分析,就能最终确定本题的解法了:

DFS+回溯+剪枝

#include <stdio.h>
#include<string.h>
int w,h;//记录场地的宽和高
int sx,sy,ex,ey;//记录起点和终点坐标
int dx[4]={0,0,-1,1};//存方向变化量
int dy[4]={1,-1,0,0};
int maps[30][30],best;//best记录最优解。maps存地图

void dfs(int cx,int cy,int step)//cx,cy记录当前位置。step表示已走多少步
{
    int nx,ny,i;
    if(step>best)//剪枝如果大于目前最优解直接返回
        return;
    for(i=0;i<4;i++)
    {
        nx=cx+dx[i];
        ny=cy+dy[i];
        if(nx>=h||nx<0||ny>=w||ny<0||maps[nx][ny]==1)//越界或立即有阻挡物剪枝
            continue;
        while(nx<h&&nx>=0&&ny<w&&ny>=0&&maps[nx][ny]!=1)//一直滑
        {
            if(nx==ex&&ny==ey)//若到终点
            {
                if(step+1<best)
                    best=step+1;
                break;
            }
            nx+=dx[i];
            ny+=dy[i];
        }
        if(nx==ex&&ny==ey)//若由于到终点跳出去
            continue;
        if(nx<h&&nx>=0&&ny<w&&ny>=0)
        {
            maps[nx][ny]=0;//若是碰到阻挡物。阻挡物消失。
            dfs(nx-dx[i],ny-dy[i],step+1);//继续搜索
            maps[nx][ny]=1;//还原阻挡物。回溯
        }
    }
}
int main()
{
    int i,j;
    while(scanf("%d%d",&w,&h),w||h)
    {
        best=12;//初始化best
        for(i=0;i<h;i++)//读取地图
            for(j=0;j<w;j++)
            {
                scanf("%d",&maps[i][j]);
                if(maps[i][j]==2)
                    sx=i,sy=j;
                if(maps[i][j]==3)
                    ex=i,ey=j;
            }
        dfs(sx,sy,0);//深搜
        if(best<=10)
            printf("%d\n",best);
        else
            printf("-1\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值