Problem:
Problem E: Mirror Cave

双子の冒険者 Rin と Len は鏡の洞窟でお宝を探している．この洞窟には鏡の間という対になった 2 つの部屋があり，その部屋の扉の向こうには高価な宝が眠っているという．

便宜上，2 つの部屋はそれぞれ W × H のセルが格子状に並んだものとみなす．部屋の外側は壁で囲まれている．また，部屋の内側にもところどころ壁が設置されており，壁の配置されているセルには進入することができない．宝の部屋への扉を開くためには，2 人が左右対称の動きをして，同時に特定のセルに到達しなければならない．しかも，片方だけが先に到達した場合は，扉はロックされて開かなくなってしまう．

左右対称の動きとは，北と北，西と東，東と西，南と南にそれぞれ同時に動くことを意味する．ただし，片方が動こうとしている先に壁があり，もう一方の先に壁が無いといった状況下でも左右対称の動きであると認められる．この場合は壁がある方はその場にとどまり，壁が無い方は隣のセルに動くことになる．

入力として，双子の初期位置，目的地，障害物の配置が与えられる．あなたの仕事は，この双子が扉を開けることが可能であるか判定するプログラムを書くことである．

Input

入力は複数のデータセットからなる．各データセットは次の形式で与えられる．

W H

RoomL1 RoomR1

RoomL2 RoomR2

...

RoomLH RoomRH

最初の行では 2 つの正の整数 W, H (1 ≤ W, H ≤ 50) が 1 つの空白で区切られて与えられる．その後，H 行に渡って 2 つの部屋の情報が与えられる．RoomLi，RoomRi はそれぞれ左側，右側の部屋の i 番目の行に対応する．両者とも長さ W の文字列で，j 番目の文字が部屋の j 番目の列に対応する．それぞれの文字は以下のいずれかである．

. : 自由に移動できるセル

# : 壁のあるセル（進入できない）

% : 目的地

R : Rin の初期位置

L : Len の初期位置

各部屋とも 1 行目が部屋の北端，1 列目が部屋の西端に対応する．また，各部屋は必ずちょうど 1 つの目的地があり，左側の部屋には Len，右側の部屋には Rin の初期位置がちょうど 1 つ存在する．

入力の終わりは，空白で区切られた 2 つの 0 を含む行で示される．

Output

各データセットについて，双子が扉を開くことができるなら Yes，できなければ No をそれぞれ 1 行に出力せよ．

Sample Input

5 5
%#... ...#%
.#.#. .#.#.
.#.#. .#.#.
.#.#. .#.#.
...#L R#...
3 2
.L. .R#
%.. .%.
4 1
L.%. %..R
0 0

Output for the Sample Input

Yes
Yes
No

Solution in C# (judge verdict: Accepted)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static System.Console;
using static System.Math;

namespace AtTest.HCPC2019_29
{
    class C
    {
        static void Main(string[] args)
        {
            var sw = new System.IO.StreamWriter(OpenStandardOutput()) { AutoFlush = false };
            SetOut(sw);

            Method(args);

            Out.Flush();
        }

        static void Method(string[] args)
        {
            int[] dx = new int[4] { 1, -1, 0, 0 };
            int[] dy = new int[4] { 0, 0, 1, -1 };
            while (true)
            {
                int[] wh = ReadInts();
                int w = wh[0];
                int h = wh[1];
                if (h == 0) break;

                bool[,] gridL, gridR;
                gridL = new bool[h, w];
                gridR = new bool[h, w];
                int[] lStart, rStart, lGoal, rGoal;
                lStart = new int[2] { 0, 0 };
                rStart = new int[2] { 0, 0 };
                lGoal = new int[2] { 0, 0 };
                rGoal = new int[2] { 0, 0 };
                for (int i = 0; i < h; i++)
                {
                    string s = Read();
                    for (int j = 0; j < w; j++)
                    {
                        gridL[i, j] = true;
                        switch (s[j])
                        {
                            case '#':
                                gridL[i, j] = false;
                                break;
                            case 'L':
                                lStart = new int[2] { i, j };
                                break;
                            case '%':
                                lGoal = new int[2] { i, j };
                                break;
                        }
                        gridR[i, j] = true;
                        switch (s[s.Length - 1 - j])
                        {
                            case '#':
                                gridR[i, j] = false;
                                break;
                            case 'R':
                                rStart = new int[2] { i, j };
                                break;
                            case '%':
                                rGoal = new int[2] { i, j };
                                break;
                        }
                    }
                }

                bool[,,,] cans = new bool[h, w, h, w];
                Queue<int[]> queue = new Queue<int[]>();
                queue.Enqueue(new int[4] { lStart[0], lStart[1],
                    rStart[0], rStart[1] });
                bool done = false;
                while (queue.Count > 0)
                {
                    int[] val = queue.Dequeue();
                    int lY = val[0];
                    int lX = val[1];
                    int rY = val[2];
                    int rX = val[3];
                    if(lY==lGoal[0]&&lX==lGoal[1]
                        && rY == rGoal[0] && rX == rGoal[1])
                    {
                        WriteLine("Yes");
                        done = true;
                        break;
                    }
                    if ((lY == lGoal[0] && lX == lGoal[1])
                        || (rY == rGoal[0] && rX == rGoal[1])) continue;
                    if (cans[lY, lX, rY, rX]) continue;

                    cans[lY, lX, rY, rX] = true;
                    for(int i = 0; i < 4; i++)
                    {
                        int llY = lY + dy[i];
                        int llX = lX + dx[i];
                        int rrY = rY + dy[i];
                        int rrX = rX + dx[i];
                        if (llY < 0 || llY >= h || !gridL[llY, lX]) llY = lY;
                        if (llX < 0 || llX >= w || !gridL[lY, llX]) llX= lX;
                        if (rrY < 0 || rrY >= h || !gridR[rrY, rX]) rrY = rY;
                        if (rrX < 0 || rrX >= w || !gridR[rY, rrX]) rrX = rX;

                        if (!cans[llY, llX, rrY, rrX])
                        {
                            queue.Enqueue(new int[4] { llY,llX,rrY,rrX });
                        }
                    }
                }
                if (!done) WriteLine("No");
            }
        }

        private static string Read() { return ReadLine(); }
        private static char[] ReadChars() { return Array.ConvertAll(Read().Split(), a => a[0]); }
        private static int ReadInt() { return int.Parse(Read()); }
        private static long ReadLong() { return long.Parse(Read()); }
        private static double ReadDouble() { return double.Parse(Read()); }
        private static int[] ReadInts() { return Array.ConvertAll(Read().Split(), int.Parse); }
        private static long[] ReadLongs() { return Array.ConvertAll(Read().Split(), long.Parse); }
        private static double[] ReadDoubles() { return Array.ConvertAll(Read().Split(), double.Parse); }
    }
}