Curling 2.0 DFS+回溯+剪枝

来源:互联网 发布:上海安畅网络 编辑:程序博客网 时间:2024/05/22 12:32

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;}


0 0
原创粉丝点击