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

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

問題

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

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


ソース

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 == 'B' || WillSetKey == 'E')
                    if (I == 0) continue;
                if (WillSetKey == 'D')
                    if (I >= 6) break;

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

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

        //秒に換算してから掛け算
        int SumSec = (wkMinute * 60 + wkSec) * (pPopped['B'] * 10 + 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;
    }
}


実行結果

解を発見
6分47秒 * 19 = 2時間08分53秒
解を発見
3分02秒 * 98 = 4時間57分16秒
解を発見
5分09秒 * 84 = 7時間12分36秒
解を発見
4分18秒 * 72 = 5時間09分36秒


解説

  E分DC秒
*    BA
として、深さ優先探索で解を求めてます。

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