Curling 2.0

题目链接:https://vjudge.net/problem/POJ-3009

Description

今年的奥运会之后,在行星mm-21上冰壶越来越受欢迎。但是规则和我们的有点不同。这个游戏是在一个冰游戏板上玩的,上面有一个正方形网格。他们只用一块石头。游戏的目的是让石子从起点到终点,并且移动的次数最小

图1显示了一个游戏板的例子。一些正方形格子可能被砖块占据。有两个特殊的格子,起始点和目标点,这是不占用块。(这两个方块是不同的)一旦石头开始移动就不会停下,除非它击中砖块块。为了使石头到达终点,你可以通过让石块击中墙壁或者砖块来停下。

图1

图1:例子(S:开始,G:目标)

石头的运动遵循以下规则:

  • 开始时,石头静止起点广场上。
  • 石头的运动仅限于x和y方向。禁止对角线移动。
  • 当石头静止时,你可以让他向任意方向移动,除非它移动的方向上有砖块(图2(a))。
  • 一旦抛出,石头不断向同一方向移动,直到下列事件之一发生:
  • 石头击中砖块(图2(b), (c))
    • 石头停在他击中的砖块之前
    • 被击中的砖块消失
  • 石块飞出游戏板之外。
    • 游戏结束的条件
  • 到达目标点
    • 石头停在目标点游戏成功
  • 不能在十步之内到达目标点则返回失败。
Fig. 2: Stone movements

通过这些规则我们想知道,石头是否能够到达目标点和最少移动次数

初始配置如图1所示,石头从开始到目标需要4次移动。路线如图3所示(a)。注意当石头到达目标时,游戏版的配置如图3(b)改变。

Fig. 3: The solution for Fig. D-1 and the final board configuration

Input

输入是一组数据。输入结束标志为两个0。数据组的数量不超过100。

每个数据集如下展示

板的宽度(w)和高度(h)
游戏版的第一行
...
游戏版的h-th行

版的宽和高满足: 2 <= w <= 20, 1 <= h <= 20.

每行由一个空格分隔的十进制数字组成。该数字描述相应的格子的状态。

1 砖块

2 开始点

3 目标点

图. D-1数据如下:

6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1

Output

对于每个数据,打印一个十进制整数的行,表示从开始到目标的路径的最小移动次数。如果没有这样的路线,打印- 1。每个行不应该有这个数字以外的任何字符。

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1

理解:

  一张地图上有障碍,要操作一个冰壶移动,冰壶在没有碰到障碍物之前会一直移动,若碰到障碍物,则障碍物消失,需要重新扔一次,题目的限制是扔的次数不能超过十次的最少扔的次数

题解

#include 
#include 
using namespace std;
int map[20][20], m, n, sy, sx;
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int ans;
void dfs(int x, int y, int step)
{
    int nx, ny, ex, ey; //nx,ny代表下一步,ex,ey代表这一步
    if (step > 10)
        return; //剪枝
    for (int i = 0; i < 4; i++)
    {
        nx = x + dir[i][0];
        ny = y + dir[i][1];
        ex = x;
        ey = y;
        /*因为没有碰到障碍物时冰壶会一直滑动,
        所以要用while来判断是否出界直到碰到障碍物再dfs,相当于之前的if*/
        //为真代表下一步可走
        while (nx >= 0 && nx < m && ny >= 0 && ny < n && map[nx][ny] != 1)
        {
            ex += dir[i][0]; //则走这一步
            ey += dir[i][1];
            if (map[ex][ey] == 3) //判断走的这一步是否是终点
            {                     //如果是
                ans = (step < ans ? step : ans);
                //比较,最后遍历完所有路径后取出最小的那个
                return;
            }
            //如果不是,继续往下走,看看是否会越界
            nx = ex + dir[i][0];
            ny = ey + dir[i][1];
            if (nx < 0 || nx >= m || ny < 0 || ny >= n)
                break;
            /*如果下一步越界,则说明会滑出去,即下下一步会直接出界,
            不用再进行dfs,跳出while循环,继续for循环,尝试其他方向*/
            if (map[nx][ny] == 1) //如果下一步是 1则进行深搜,否则继续进行下去
            {
                map[nx][ny] = 0;
                dfs(ex, ey, step + 1);
                map[nx][ny] = 1;
                //退栈时复原障碍物,因为换一条路走之后障碍物互不影响
            }
        }
    }
}
int main()
{
    while (cin >> n >> m)
    {
        if (m == 0 && n == 0)
            break;
        else
        {
            for (int i = 0; i < m; i++)
                for (int j = 0; j < n; j++)
                {
                    cin >> map[i][j];
                    if (map[i][j] == 2)
                    {
                        sx = i;
                        sy = j;
                    }
                }
            ans = 100000;
            dfs(sx, sy, 1);
            if (ans == 100000)
                cout << -1 << endl;
            else
                cout << ans << endl;
        }
    }
    return 0;
}

感悟:

  不要一味的地套公式,dfs算法的判断条件要根据情况而定,此题中的判断条件是该冰壶一直滑,而不是下一个位置。

你可能感兴趣的:(Curling 2.0)