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

Problem491 11で割り切れるダブルパンデジタル数

問題

0から9の数字をちょうど2回使った(先行ゼロのない)正整数をダブルパンデジタル数と呼ぼう.
例えば, 40561817703823564929 はそのような数の一つである.

11で割り切れるダブルパンデジタル数はいくつあるか?


ソース

using System;
using System.Collections.Generic;

class Program
{
    const int KetaSuu = 20;
    const int Hou = 11;

    //4進数で管理するDPの状態
    //4の14乗=2の28乗なので、4進数ならオーバーフローしない
    struct JyoutaiDef
    {
        internal int ModVal; //11を法とした余り
        internal int[] AppearedCntArr; //0から9の登場数
    }

    static int GetHash(JyoutaiDef pJyoutai)
    {
        int WillReturn = 0;
        WillReturn += pJyoutai.ModVal;

        foreach (int EachInt in pJyoutai.AppearedCntArr) {
            WillReturn *= 4;
            WillReturn += EachInt;
        }
        return WillReturn;
    }

    static JyoutaiDef GetJyoutai(int pHash)
    {
        int CopiedHash = pHash;

        JyoutaiDef WillReturn;
        WillReturn.AppearedCntArr = new int[10];
        for (int I = 9; 0 <= I; I--) {
            WillReturn.AppearedCntArr[I] = CopiedHash % 4;
            CopiedHash /= 4;
        }
        WillReturn.ModVal = CopiedHash;
        return WillReturn;
    }

    static void Main()
    {
        //10進数での桁ごとの11を法とした余りを求めておく
        DeriveKetaModDict();

        //場合の数[状態のハッシュ値]なDP表
        var PrevDP = new Dictionary<int, long>();
        PrevDP.Add(0, 1);

        for (int I = 1; I <= KetaSuu; I++) {
            var CurrDP = new Dictionary<int, long>();

            foreach (var EachPair in PrevDP) {
                for (int J = 0; J <= 9; J++) {
                    //1桁目の0は不可
                    if (I == 1 && J == 0) continue;

                    JyoutaiDef CurrJyoutai = GetJyoutai(EachPair.Key);

                    CurrJyoutai.ModVal += mKetaModDict[I] * J;
                    CurrJyoutai.ModVal %= Hou;

                    if (CurrJyoutai.AppearedCntArr[J] == 2)
                        continue;
                    CurrJyoutai.AppearedCntArr[J]++;

                    int NewKey = GetHash(CurrJyoutai);
                    if (CurrDP.ContainsKey(NewKey))
                        CurrDP[NewKey] += EachPair.Value;
                    else CurrDP[NewKey] = EachPair.Value;
                }
            }
            PrevDP = CurrDP;
        }
        long Answer = 0;
        foreach (var EachPair in PrevDP) {
            JyoutaiDef wkJyoutai = GetJyoutai(EachPair.Key);

            if (wkJyoutai.ModVal > 0) continue;

            if (Array.TrueForAll(wkJyoutai.AppearedCntArr, X => X == 2))
                Answer += EachPair.Value;
        }
        Console.WriteLine("解は{0}通り", Answer);
    }

    //10進数での桁ごとの11を法とした余りを求めておく
    static Dictionary<int, int> mKetaModDict = new Dictionary<int, int>();
    static void DeriveKetaModDict()
    {
        int ModVal = 1;
        for (int I = KetaSuu; 1 <= I; I--) {
            mKetaModDict[I] = ModVal;

            ModVal *= 10;
            ModVal %= Hou;
        }
    }
}


実行結果

解は194505988824000通り


解説

BitDPで、桁DPしてます。