Problem:
B: サイコロを転がさないで

問題文

$H$ 行 $W$ 列からなるグリッドがあります。以降、グリッド上の $i$ 行目 $j$ 列目のマスを $(i, j)$ のマスと書きます。

グリッドの各マスには、$1$ 以上 $6$ 以下の数字か、あるいは # の文字が $1$ つずつ書かれています。ただし、$i$ と $j$ がともに偶数であるような $(i, j)$ のマスには必ず # が書かれています。

あなたは以下の図で表されるようなサイコロを $1$ つ持っています。

最初、あなたはこのサイコロを底面が $6$, 前面が $2$, 右面が $3$ となるように $(1, 1)$ のマスに置きます。
ここで、前面は $(i, j)$ の $i$ が増える方向の面を、右面は $j$ が増える方向の面を指します。

その後、サイコロが置かれているマスに辺で隣接する $4$ マスのいずれかにサイコロを転がす操作を好きな回数繰り返すことができます。
サイコロを転がす際、サイコロは転がす方向に90度回転します。

ただし、サイコロを転がす際には以下の条件を満たしている必要があります。

グリッドの外にサイコロを転がしてはならない

# が書かれているマスに転がしてはならない

転がした後の状態を考えたときに、サイコロの底面に書かれた数字とそのマスに書かれた数字が一致する

$(1, 1)$ のマスには必ず $6$ が書かれていることが保証されます。

サイコロを転がす操作を繰り返すことで、サイコロを $(1, 1)$ のマスから $(H, W)$ のマスに移動させることができるか判定してください。

制約

$1 \leq H, W \leq 100$

グリッドの各マスには $1$ 以上 $6$ 以下の数字、または # が書かれている

$i, j$ がともに偶数となるような $(i, j)$ のマスには必ず # が書かれている

$(1, 1)$ には $6$ が書かれていることが保証される

入力

入力は以下の形式で標準入力から与えられる。

$H$ $W$
$s_{11}s_{12} \ldots s_{1W}$
$s_{21}s_{22} \ldots s_{2W}$
$\vdots$
$s_{H1}s_{H2} \ldots s_{HW}$

ここで、$s_{ij}$ は $(i, j)$ のマスにかかれている数字または文字を表す。
すなわち、$s_{ij}$ は $1$ 以上 $6$ 以下の数字であるか、あるいは # である。

出力

$(1, 1)$ のマスから $(H, W)$ のマスへサイコロを転がして移動させることができるならば YES を、そうでないならば NO を 1 行で出力せよ。

入力例 1

3 3
631
4#2
516

出力例 1

YES

$(1, 1), (1, 2), (1, 3), (2, 3), (3, 3)$ の順に転がすことで、到達可能です。

入力例 2

3 3
6#1
##2
516

出力例 2

NO

入力例 3

5 5
61244
2#5#3
14641
5#5#5
63126

出力例 3

YES

Solution in C# (judge verdict: Accepted)

using System.Collections.Generic;
using System.Linq;
using System;

public class P
{
    public string d { get; set; }
    public int x { get; set; }
    public int y { get; set; }
}

public class DICE
{
    private int[] data;
    public DICE(string s = "1,5,6,4,2,3")
    {
        data = new int[6];
        string[] line = s.Split(',');
        for (int i = 0; i < 6; i++)
            data[i] = int.Parse(line[i]);        // 0: ceilling 1: front 2: floor 3:right:4:back:5:left
    }
    public void roll(int c)
    {
        var w = new int[6];
        Array.Copy(data, w, 6);
        if (c == 3) { data[4] = w[0]; data[0] = w[1]; data[1] = w[2]; data[2] = w[4]; }
        else if (c == 1) { data[1] = w[0]; data[2] = w[1]; data[4] = w[2]; data[0] = w[4]; }
        else if (c == 0) { data[3] = w[0]; data[5] = w[2]; data[2] = w[3]; data[0] = w[5]; }
        else { data[5] = w[0]; data[3] = w[2]; data[0] = w[3]; data[2] = w[5]; }
    }
    public int peek(int n) => data[n];
    public string getDice() => string.Join(",", data);
}


public class hello
{
    public static void Main()
    {
        string[] line = Console.ReadLine().Trim().Split(' ');
        var h = int.Parse(line[0]);
        var w = int.Parse(line[1]);
        var map = new int[h, w];
        for (int i = 0; i < h; i++)
        {
            var s = Console.ReadLine().Trim();
            for (int j = 0; j < w; j++)
                if (s[j] != '#') map[i, j] = s[j] - '0';
        }
        Console.WriteLine(getAns(h, w, map) ? "YES" : "NO");
    }
    static bool getAns(int h, int w, int[,] map)
    {
        var dx = new int[] { 0, 1, 0, -1 };
        var dy = new int[] { 1, 0, -1, 0 };
        var visited = new bool[h, w];
        var q = new Queue<P>();
        q.Enqueue(new P { d = "1,2,6,3,5,4", x = 0, y = 0 });
        visited[0, 0] = true;
        while (q.Count() > 0)
        {
            var t = q.Dequeue();
            if (t.x == h - 1 && t.y == w - 1) return true;
            for (int i = 0; i < 4; i++)
            {
                var nx = t.x + dx[i];
                var ny = t.y + dy[i];
                if (nx >= 0 && nx < h && ny >= 0 && ny < w && map[nx, ny] != 0 && !visited[nx, ny])
                {
                    var dice = new DICE(t.d);
                    dice.roll(i);
                    if (dice.peek(2) == map[nx, ny])
                    {
                        visited[nx, ny] = true;
                        q.Enqueue(new P { d = dice.getDice(), x = nx, y = ny });
                    }
                }
            }
        }
        return false;
    }
}