トップページに戻る    次のC#のサンプルへ    前のC#のサンプルへ

コンピュータパズルへの招待 2問目 時を掛けるパズル1

問題

Fig.Aの□を埋めてほしい。
それぞれ0〜9を1字ずつで御願いする。
分、秒には60以上の数が入らないことに注意。
ただし、*には0は入らない。

Fig.A 時を掛けるパズル1


ソース

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

class Program
{
    static void Main()
    {
        var stk = new Stack<Dictionary<char, int>>();
        for (int I = 0; I <= 9; I++)
            stk.Push(new Dictionary<char, int>() { { 'A', I } });

        while (stk.Count > 0) {
            Dictionary<char, int> Popped = stk.Pop();

            if (new HashSet<char>("ABCDE").SetEquals(Popped.Keys)) {
                GoalSyori(Popped);
                continue;
            }

            var wkSet = new HashSet<char>("ABCDE");
            wkSet.ExceptWith(Popped.Keys);
            char WillSetKey = wkSet.Min();
            for (int I = 0; I <= 9; I++) {
                if (WillSetKey == 'A' || WillSetKey == 'E')
                    if (I == 0) continue;
                if (WillSetKey == 'C' || WillSetKey == 'E')
                    if (I >= 6) break;

                stk.Push(new Dictionary<char, int>(Popped) { { WillSetKey, I } });
            }
        }
    }

    //解に到達したら解を表示
    static void GoalSyori(Dictionary<char, int> pPopped)
    {
        int wkSec = pPopped['C'] * 10 + pPopped['B'];
        int wkMinute = pPopped['E'] * 10 + pPopped['D'];

        //秒に換算してから掛け算
        int SumSec = (wkMinute * 60 + wkSec) * pPopped['A'];

        int[] ResultArr = DeriveResultArr(SumSec);

        //10時間以上の場合はNG
        if (ResultArr[0] >= 10) return;
        //1時間未満の場合はNG
        if (ResultArr[0] < 1) return;

        //有効な数値リストかを判定
        if (IsValidNums(pPopped.Values.Concat(ResultArr)) == false) return;

        Console.WriteLine("解を発見");
        Console.Write("{0}{1}分{2}{3}秒 * {4} = ",
            pPopped['E'], pPopped['D'], pPopped['C'], pPopped['B'], pPopped['A']);
        Console.WriteLine("{0}時間{1}{2}分{3}{4}秒",
            ResultArr[0], ResultArr[1], ResultArr[2], ResultArr[3], ResultArr[4]);
    }

    //秒を引数として、時分秒に換算した配列を返す
    static int[] DeriveResultArr(int pSumSec)
    {
        int[] WillReturn = new int[5];

        //時
        int wkHour = pSumSec / 3600;
        WillReturn[0] = wkHour;
        pSumSec %= 3600;

        //分
        int wkMinute = pSumSec / 60;
        WillReturn[1] = wkMinute / 10;
        WillReturn[2] = wkMinute % 10;
        pSumSec %= 60;

        //秒
        int wkSecond = pSumSec;
        WillReturn[3] = wkSecond / 10;
        WillReturn[4] = wkSecond % 10;

        return WillReturn;
    }

    //有効な数値リストかを判定
    static bool IsValidNums(IEnumerable<int> pNumList)
    {
        bool[] IsAppearedArr = new bool[10];

        foreach (int AnyInt in pNumList) {
            //同じ数字を2個以上含んだらNG
            if (IsAppearedArr[AnyInt]) return false;
            IsAppearedArr[AnyInt] = true;
        }
        return true;
    }
}


実行結果

解を発見
50分42秒 * 9 = 7時間36分18秒


解説

 ED分CB秒
*     A
として、深さ優先探索で解を求めてます。

秒の1桁目や2桁目が確定した時点での枝切りを考えてましたが
枝切りが不要な処理速度でした。