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

Cマガ電脳クラブ(第042回) 平方数の和

問題

1から9までの数字を1字ずつ使って、たとえば157326849という数字を作る。
これは12543の2乗、つまり平方数である。

さて、35721と8649というふたつの数を作っても、これまたどちらも平方数である。
では1から9までの数字を1字ずつ使って平方数を作ったとき、できた平方数の総和を得点と定義する。
上記の35721と8649の場合、44370点である。
この場合は、ふたつの平方数に分けたが、もっと多くの個数にすることもできる。
では最小得点になるものを探してほしい。


ソース

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

class Program
{
    //得点の上限
    static int TokutenJyougen = 9999;

    //候補の平方数リスト
    static List<int> HeihouSuuList = new List<int>();

    struct JyoutaiDef
    {
        internal int CurrP;
        internal List<int> ValList;
    }

    static void Main()
    {
        Console.WriteLine("得点の上限を{0}として検証します。",TokutenJyougen);
        Console.WriteLine();

        for (int I = 1; I * I <= TokutenJyougen; I++)
            HeihouSuuList.Add(I * I);

        var stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        for (int P = 0; P <= HeihouSuuList.Count - 1; P++) {
            WillPush.CurrP = P;
            WillPush.ValList = new List<int>() { HeihouSuuList[P] };
            stk.Push(WillPush);
        }

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

            int StrLengthSum = Popped.ValList.Sum(X => X.ToString().Length);

            if (StrLengthSum == 9) {
                int Tokuten = Popped.ValList.Sum();
                if (TokutenJyougen < Tokuten) continue;
                TokutenJyougen = Tokuten;

                PrintAnswer(Popped.ValList);
                continue;
            }

            for (int P = Popped.CurrP + 1; P <= HeihouSuuList.Count - 1; P++) {
                WillPush.CurrP = P;
                WillPush.ValList = new List<int>(Popped.ValList) { HeihouSuuList[P] };
                if (IsValidVals(WillPush.ValList))
                    stk.Push(WillPush);
            }

        }
    }

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

        foreach (int AnyInt in pValList) {
            int CopiedVal = AnyInt;
            do {
                int ModVal = CopiedVal % 10;

                //0を含んだらNG
                if (ModVal == 0) return false;

                //同じ数字を2個以上含んだらNG
                if (IsAppearedArr[ModVal]) return false;
                IsAppearedArr[ModVal] = true;

                CopiedVal /= 10;
            } while (CopiedVal > 0);
        }
        return true;
    }

    //解を表示
    static void PrintAnswer(List<int> pValList)
    {
        //平方根を求める
        Func<int, int> GetHeihouKon = (pVal) =>
        {
            return (int)Math.Sqrt(pVal);
        };

        Console.WriteLine("得点が{0}の解を発見", pValList.Sum());
        pValList.ForEach(X =>
            Console.Write("{0}({1}の2乗) , ", X, GetHeihouKon(X)));
        Console.WriteLine();
    }
}


実行結果

得点の上限を9999として検証します。

得点が1674の解を発見
361(19の2乗) , 529(23の2乗) , 784(28の2乗) ,
得点が990の解を発見
9(3の2乗) , 81(9の2乗) , 324(18の2乗) , 576(24の2乗) ,
得点が855の解を発見
1(1の2乗) , 9(3の2乗) , 25(5の2乗) , 36(6の2乗) , 784(28の2乗) ,


解説

得点の上限を仮決めしてから、平方数を列挙して、深さ優先探索してます。
深さ優先探索中に、パンデジタル数になりうるかを条件として、枝切りしてます。