AtCoderのPAST
次のPASTの問題へ
前のPASTの問題へ
第19回PAST G 結合律
C#のソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static string InputPattern = "InputX";
static List<string> GetInputList()
{
var WillReturn = new List<string>();
if (InputPattern == "Input1") {
WillReturn.Add("3");
WillReturn.Add("1 2 3");
WillReturn.Add("2 0 1");
WillReturn.Add("3 1 2");
//1
}
else if (InputPattern == "Input2") {
WillReturn.Add("3");
WillReturn.Add("2 1 0");
WillReturn.Add("1 2 3");
WillReturn.Add("1 3 2");
//0
}
else if (InputPattern == "Input3") {
WillReturn.Add("6");
WillReturn.Add("4 5 5 2 4 2");
WillReturn.Add("5 2 2 4 5 4");
WillReturn.Add("5 2 0 4 5 4");
WillReturn.Add("2 4 4 5 2 5");
WillReturn.Add("4 5 5 2 4 2");
WillReturn.Add("2 4 4 5 2 5");
//2
}
else {
string wkStr;
while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
}
return WillReturn;
}
static long[] GetSplitArr(string pStr)
{
return (pStr == "" ? new string[0] : pStr.Split(' ')).Select(pX => long.Parse(pX)).ToArray();
}
static int[,] mBanArr;
static int UB;
static int mN;
static void Main()
{
List<string> InputList = GetInputList();
mN = int.Parse(InputList[0]);
mBanArr = CreateBanArr(InputList.Skip(1));
UB = mBanArr.GetUpperBound(0);
int Pos0X = -1;
int Pos0Y = -1;
for (int Y = 0; Y <= UB; Y++) {
for (int X = 0; X <= UB; X++) {
if (mBanArr[X, Y] == 0) {
Pos0X = X;
Pos0Y = Y;
}
}
}
var KSet = new HashSet<int>();
KSet.Add(Pos0Y + 1);
for (int J = 1; J <= mN; J++) {
for (int K = 1; K <= mN; K++) {
if (GetFuncResult(J, K) == Pos0Y) {
KSet.Add(K);
}
}
}
var KList = new List<int>();
for (int I = 1; I <= mN; I++) {
KList.Add(I);
}
int Answer = 0;
for (int LoopSetVal = 1; LoopSetVal <= mN; LoopSetVal++) {
mBanArr[Pos0X, Pos0Y] = LoopSetVal;
bool IsOK = true;
for (int I = 1; I <= mN; I++) {
for (int J = 1; J <= mN; J++) {
bool UseSkipLoop = true;
if (LoopSetVal == 1) UseSkipLoop = false;
if (I == Pos0X + 1) UseSkipLoop = false;
if (J == Pos0X + 1) UseSkipLoop = false;
if (GetFuncResult(I, J) == Pos0X + 1) UseSkipLoop = false;
if (J == Pos0Y + 1) UseSkipLoop = false;
IEnumerable<int> EnumK = KList;
if (UseSkipLoop) {
EnumK = KSet;
}
foreach (int EachK in EnumK) {
int Result1 = GetFuncResult(GetFuncResult(I, J), EachK);
int Result2 = GetFuncResult(I, GetFuncResult(J, EachK));
if (Result1 != Result2) {
IsOK = false;
goto label;
}
}
}
}
label:
if (IsOK) {
Answer++;
}
}
Console.WriteLine(Answer);
}
static int GetFuncResult(int p1, int p2)
{
return mBanArr[p2 - 1, p1 - 1];
}
////////////////////////////////////////////////////////////////
// IEnumerable<string>をintの2次元配列に設定
////////////////////////////////////////////////////////////////
static int[,] CreateBanArr(IEnumerable<string> pStrEnum)
{
var StrList = pStrEnum.ToList();
if (StrList.Count == 0) {
return new int[0, 0];
}
int[] IntArr = { };
Action<string> SplitAct = pStr =>
IntArr = pStr.Split(' ').Select(pX => int.Parse(pX)).ToArray();
SplitAct(StrList[0]);
int UB_X = IntArr.GetUpperBound(0);
int UB_Y = StrList.Count - 1;
int[,] WillReturn = new int[UB_X + 1, UB_Y + 1];
for (int Y = 0; Y <= UB_Y; Y++) {
SplitAct(StrList[Y]);
for (int X = 0; X <= UB_X; X++) {
WillReturn[X, Y] = IntArr[X];
}
}
return WillReturn;
}
}
解説
F(I , F(J,K) ) = F( F(I,J) , K )
の結合法則が成り立つかの判定を
0が記述された座標の値を1からNに変更して繰り返し行うので
計算量を
O(N**4)から減らすことを考えます。
上記の式に着目すると
0が記述された座標の値が結果に影響を与えるのは、
下記の少なくとも1つを満たすときと分かります。
●0が記述されたX座標 = I
●0が記述されたX座標 = J
●0が記述されたX座標 = F(I,J)
●0が記述されたY座標 = K
●0が記述されたY座標 = F(J,K)
●0が記述されたY座標 = J
なので、I,J,Kの3重ループの一番外側のKのループ
で使うKを増減させて計算量を減らしてます。