トップページに戻る    次の増井さんの書籍の問題へ    前の増井さんの書籍の問題へ

Q47 オンリーワンな○×


C#のソース

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

class Program
{
    const int UB = 4 - 1;

    struct JyoutaiDef
    {
        internal int CurrX;
        internal int CurrY;
        internal char[,] BanArr;
        internal int[] YokoSumArr;
        internal int[] TateSumArr;
    }

    static void Main()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();

        var stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        WillPush.BanArr = new char[UB + 1, UB + 1];
        for (int X = 0; X <= UB; X++)
            for (int Y = 0; Y <= UB; Y++)
                WillPush.BanArr[X, Y] = ' ';

        WillPush.CurrX = WillPush.CurrY = 0;
        WillPush.YokoSumArr = new int[UB + 1];
        WillPush.TateSumArr = new int[UB + 1];
        stk.Push(WillPush);

        var AnswerJyoutaiList = new List<JyoutaiDef>();

        while (stk.Count > 0) {
            JyoutaiDef Popped = stk.Pop();

            //X座標の繰上げ処理
            if (Popped.CurrX > UB) {
                Popped.CurrX = 0;
                Popped.CurrY++;
            }

            //最終行を超えた場合
            if (Popped.CurrY > UB) {
                //PrintBan(Popped.BanArr);
                AnswerJyoutaiList.Add(Popped);
                continue;
            }

            Action<char> PushSyori = pChar =>
            {
                WillPush.CurrX = Popped.CurrX + 1;
                WillPush.CurrY = Popped.CurrY;
                WillPush.BanArr = (char[,])Popped.BanArr.Clone();
                WillPush.BanArr[Popped.CurrX, Popped.CurrY] = pChar;

                if (pChar == '○') {
                    WillPush.YokoSumArr = (int[])Popped.YokoSumArr.Clone();
                    WillPush.TateSumArr = (int[])Popped.TateSumArr.Clone();
                    WillPush.YokoSumArr[Popped.CurrY]++;
                    WillPush.TateSumArr[Popped.CurrX]++;

                    //横方向での○の数を(広義の)単調減少とする
                    for (int I = 0; I <= Popped.CurrY - 1; I++) {
                        if (WillPush.YokoSumArr[I] < WillPush.YokoSumArr[I + 1])
                            return;
                    }
                }
                else {
                    WillPush.YokoSumArr = Popped.YokoSumArr;
                    WillPush.TateSumArr = Popped.TateSumArr;
                }
                stk.Push(WillPush);
            };
            PushSyori('○'); 
            PushSyori('×');
        }
        Console.WriteLine("解は{0}通り。経過時間={1}",
            CntOnlyOne(AnswerJyoutaiList), sw.Elapsed);
    }

    //オンリーワンな配置を数える
    static int CntOnlyOne(List<JyoutaiDef> pAnswerJyoutaiList)
    {
        int WillReturn = 0;
        for (int I = 0; I <= pAnswerJyoutaiList.Count - 1; I++) {
            bool IsOnlyOne = true;
            for (int J = 0; J <= pAnswerJyoutaiList.Count - 1; J++) {
                if (I == J) continue;
                if (pAnswerJyoutaiList[I].YokoSumArr.
                    SequenceEqual(pAnswerJyoutaiList[J].YokoSumArr) == false)
                    continue;
                if (pAnswerJyoutaiList[I].TateSumArr.
                    SequenceEqual(pAnswerJyoutaiList[J].TateSumArr) == false)
                    continue;

                IsOnlyOne = false;
                break;
            }
            if (IsOnlyOne) {
                //横方向での○の数の(広義の)単調減少を解除し、場合の数を調整
                int[] wkArr = pAnswerJyoutaiList[I].YokoSumArr;
                int wkPatternCnt = DeriveKaijyou(wkArr.Length);
                foreach (int EachCnt in wkArr.GroupBy(X => X).Select(X => X.Count())) {
                    wkPatternCnt /= DeriveKaijyou(EachCnt);
                }
                WillReturn += wkPatternCnt;
            }
        }
        return WillReturn;
    }

    //階乗を求める
    static int DeriveKaijyou(int pTarget)
    {
        int WillReturn = 1;
        for (int I = 2; I <= pTarget; I++)
            WillReturn *= I;
        return WillReturn;
    }

    //盤面を出力
    static void PrintBan(char[,] pBanArr)
    {
        var sb = new System.Text.StringBuilder();

        for (int Y = 0; Y <= UB; Y++) {
            for (int X = 0; X <= UB; X++) {
                sb.Append(pBanArr[X, Y]);
            }
            sb.AppendLine();
        }
        Console.WriteLine(sb.ToString());
    }
}


実行結果

解は6902通り。経過時間=00:00:25.2248433


解説

○と×を深さ優先探索で敷き詰めてます。

横方向での○の数を(広義の)単調減少とし、
最後に、同じものを含む場合の順列の公式で、場合の数を調整してます。