using System; using System.Collections.Generic; using System.Linq; class Program { static string InputPattern = "Input4"; static List GetInputList() { var WillReturn = new List(); if (InputPattern == "Input1") { WillReturn.Add("1 2 3 4"); WillReturn.Add("5 6 7 8"); WillReturn.Add("9 10 11 12"); WillReturn.Add("13 14 15 0"); //Yes //初期配置がそのまま目標の配置である。 } else if (InputPattern == "Input2") { WillReturn.Add("1 2 3 4"); WillReturn.Add("5 6 7 8"); WillReturn.Add("9 10 12 0"); WillReturn.Add("13 14 11 15"); //Yes } else if (InputPattern == "Input3") { WillReturn.Add("1 2 3 4"); WillReturn.Add("5 6 7 8"); WillReturn.Add("9 10 12 15"); WillReturn.Add("13 14 11 0"); //No //目標の配置を作るためには、15の駒を最低2回はスライドしなければならない。 } else { string wkStr; while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr); } return WillReturn; } const int UB = 4 - 1; //No.228 ゆきこちゃんの15パズル static void Main() { List InputList = GetInputList(); int[,] BanArr = new int[UB + 1, UB + 1]; for (int Y = 0; Y <= UB; Y++) { int[] wkArr = InputList[Y].Split(' ').Select(X => int.Parse(X)).ToArray(); for (int X = 0; X <= UB; X++) { BanArr[X, Y] = wkArr[X]; } } Console.WriteLine(HasAnswer(BanArr) ? "Yes" : "No"); } struct JyoutaiDef { internal int[,] BanArr; internal HashSet MovedNumSet; } //深さ優先探索で解を探す static bool HasAnswer(int[,] pBanArr) { //正位置とのマンハッタン距離が2以上の数値があったらNo for (int LoopX = 0; LoopX <= UB; LoopX++) { for (int LoopY = 0; LoopY <= UB; LoopY++) { if (pBanArr[LoopX, LoopY] == 0) continue; if (DeriveKyori(pBanArr[LoopX, LoopY], LoopX, LoopY) >= 2) return false; } } var stk = new Stack(); JyoutaiDef WillPush; WillPush.BanArr = pBanArr; WillPush.MovedNumSet = new HashSet(); stk.Push(WillPush); while (stk.Count > 0) { JyoutaiDef Popped = stk.Pop(); //PrintBan(Popped.BanArr); //クリア判定 if (IsClear(Popped.BanArr)) return true; int ZeroX, ZeroY; Derive0Zahyou(Popped.BanArr, out ZeroX, out ZeroY); Action PushSyori = (pFromX, pFromY) => { if (pFromX < 0 || UB < pFromX) return; if (pFromY < 0 || UB < pFromY) return; //1度移動した数値は移動不可 if (Popped.MovedNumSet.Contains(pBanArr[pFromX, pFromY])) return; //正位置とのマンハッタン距離が1の数値のみ移動可 if (DeriveKyori(pBanArr[pFromX, pFromY], pFromX, pFromY) != 1) return; WillPush.BanArr = (int[,])Popped.BanArr.Clone(); int SankakuWK = WillPush.BanArr[pFromX, pFromY]; WillPush.BanArr[pFromX, pFromY] = 0; WillPush.BanArr[ZeroX, ZeroY] = SankakuWK; WillPush.MovedNumSet = new HashSet(Popped.MovedNumSet) { SankakuWK }; stk.Push(WillPush); }; PushSyori(ZeroX, ZeroY - 1); PushSyori(ZeroX, ZeroY + 1); PushSyori(ZeroX - 1, ZeroY); PushSyori(ZeroX + 1, ZeroY); } return false; } //数値の現座標を引数として、正位置とのマンハッタン距離を求める static int DeriveKyori(int pCurrNum, int pX, int pY) { int NeedNum = 1; for (int LoopY = 0; LoopY <= UB; LoopY++) { for (int LoopX = 0; LoopX <= UB; LoopX++) { if (pCurrNum == NeedNum++) { return Math.Abs(LoopX - pX) + Math.Abs(LoopY - pY); } } } return -1; } //盤面の0の座標を返す static void Derive0Zahyou(int[,] pBanArr, out int pX, out int pY) { pX = pY = -1; for (int LoopX = 0; LoopX <= UB; LoopX++) { for (int LoopY = 0; LoopY <= UB; LoopY++) { if (pBanArr[LoopX, LoopY] != 0) continue; pX = LoopX; pY = LoopY; return; } } } //クリア判定 static bool IsClear(int[,] pBanArr) { int NeedNum = 1; for (int LoopY = 0; LoopY <= UB; LoopY++) { for (int LoopX = 0; LoopX <= UB; LoopX++) { if (LoopX == UB && LoopY == UB) { if (pBanArr[LoopX, LoopY] != 0) return false; } else if (pBanArr[LoopX, LoopY] != NeedNum++) return false; } } return true; } //盤面を表示 static void PrintBan(int[,] pBanArr) { Console.WriteLine(new string('■', 15)); for (int LoopY = 0; LoopY <= UB; LoopY++) { for (int LoopX = 0; LoopX <= UB; LoopX++) { Console.Write("{0,2},", pBanArr[LoopX, LoopY]); } Console.WriteLine(); } } }