DPコンテスト    次のDPコンテストの問題へ    前のDPコンテストの問題へ

TDP M 家


問題へのリンク


C#のソース

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

class Program
{
    static string InputPattern = "InputX";

    static List<string> GetInputList()
    {
        var WillReturn = new List<string>();

        if (InputPattern == "Input1") {
            WillReturn.Add("10 2");
            WillReturn.Add("0 1");
            WillReturn.Add("1 0");
            //512
        }
        else if (InputPattern == "Input2") {
            WillReturn.Add("2 5");
            WillReturn.Add("0 1 1 1 1");
            WillReturn.Add("1 0 1 1 1");
            WillReturn.Add("1 1 0 1 1");
            WillReturn.Add("1 1 1 0 1");
            WillReturn.Add("1 1 1 1 0");
            //1025
        }
        else {
            string wkStr;
            while ((wkStr = Console.ReadLine()) != null) WillReturn.Add(wkStr);
        }
        return WillReturn;
    }

    const long Hou = 1000000007;

    static long mH;
    static long mR;
    static long[,] mEdgeArr;
    static long UB;

    static void Main()
    {
        List<string> InputList = GetInputList();
        long[] wkArr = InputList[0].Split(' ').Select(pX => long.Parse(pX)).ToArray();
        mH = wkArr[0];
        mR = wkArr[1];

        mEdgeArr = CreateBanArr(InputList.Skip(1));
        UB = mEdgeArr.GetUpperBound(0);

        long Result = Solve();
        Console.WriteLine(Result);
    }

    static long Solve()
    {
        // 1階しかない場合
        if (mH == 1) return 1;

        // 場合の数 [開始ノード,終点ノード] な配列
        long[,] PatternArr = new long[UB + 1, UB + 1];

        for (long I = 0; I <= UB; I++) {
            long[,] TSPResult = ExecTSP(I);
            for (long J = 0; J <= TSPResult.GetUpperBound(0); J++) {
                for (long K = 0; K <= TSPResult.GetUpperBound(1); K++) {
                    PatternArr[I, J] += TSPResult[J, K];
                }
            }
        }

        long[,] FirstMatrix = new long[UB + 1, UB + 1];
        for (long I = 0; I <= UB; I++) {
            FirstMatrix[0, I] = PatternArr[0, I];
        }
        long[,] LastMatrix = new long[UB + 1, UB + 1];
        for (long I = 0; I <= UB; I++) {
            LastMatrix[I, 0] = PatternArr[I, 0];
        }

        // 行列の積は、結合法則が成り立つので、最初と最後以外は、繰り返し二乗法を使う
        long[,] BekijyouMatrix = DeriveBekijyouMatrix(PatternArr, mH - 2);

        long[,] AnswerMatrix = DeriveMatrixProd(FirstMatrix, BekijyouMatrix);
        AnswerMatrix = DeriveMatrixProd(AnswerMatrix, LastMatrix);

        long Answer = 0;
        for (long I = 0; I <= UB; I++) {
            Answer += AnswerMatrix[I, 0];
            Answer %= Hou;
        }
        return Answer;
    }

    // 始点ノードを引数として、場合の数 [現在ノード , 訪問済ノードセット] な配列を返す
    static long[,] ExecTSP(long pStaNode)
    {
        long AllBitOn = (1 << (int)mR) - 1;

        // 場合の数 [現在ノード , 訪問済ノードセット] なDP表
        long[,] PrevDP = new long[UB + 1, AllBitOn + 1];
        PrevDP[pStaNode, 1 << (int)pStaNode] = 1;

        long[,] WillReturn = new long[UB + 1, AllBitOn + 1];
        WillReturn[pStaNode, 1 << (int)pStaNode] = 1;

        for (long I = 1; I <= mR - 1; I++) { // 移動回数でループ
            long[,] CurrDP = new long[UB + 1, AllBitOn + 1];
            for (long J = 0; J <= UB; J++) {
                for (long K = 0; K <= AllBitOn; K++) {
                    if (PrevDP[J, K] == 0) continue;

                    for (long L = 0; L <= UB; L++) { // 移動先ノード
                        if (mEdgeArr[J, L] == 0) continue;

                        // ビットシフトする必要あり
                        long LBit = (1 << (int)L);

                        // 再訪は不可
                        if ((K & LBit) > 0) continue;

                        long NewJ = L;
                        long NewK = K | LBit;
                        CurrDP[NewJ, NewK] += PrevDP[J, K];
                        CurrDP[NewJ, NewK] %= Hou;
                        WillReturn[NewJ, NewK] += PrevDP[J, K];
                        WillReturn[NewJ, NewK] %= Hou;
                    }
                }
            }
            PrevDP = CurrDP;
        }
        return WillReturn;
    }

    // 正方行列2つの積を返す
    static long[,] DeriveMatrixProd(long[,] pMatrix1, long[,] pMatrix2)
    {
        long UB = pMatrix1.GetUpperBound(0);

        // 横の値の配列[行]
        var YokoArrDict = new Dictionary<long, long[]>();
        for (long LoopY = 0; LoopY <= UB; LoopY++) {
            var YokoList = new List<long>();
            for (long LoopX = 0; LoopX <= UB; LoopX++) {
                YokoList.Add(pMatrix1[LoopY, LoopX] % Hou);
            }
            YokoArrDict[LoopY] = YokoList.ToArray();
        }

        // 縦の値の配列[列]
        var TateArrDict = new Dictionary<long, long[]>();
        for (long LoopX = 0; LoopX <= UB; LoopX++) {
            var TateList = new List<long>();
            for (long LoopY = 0; LoopY <= UB; LoopY++) {
                TateList.Add(pMatrix2[LoopY, LoopX] % Hou);
            }
            TateArrDict[LoopX] = TateList.ToArray();
        }

        long[,] MatrixProd = new long[UB + 1, UB + 1];
        for (long LoopY = 0; LoopY <= UB; LoopY++) {
            for (long LoopX = 0; LoopX <= UB; LoopX++) {
                long SumVal = 0;
                for (long K = 0; K <= UB; K++) {
                    SumVal += (YokoArrDict[LoopY][K] * TateArrDict[LoopX][K] % Hou) % Hou;
                    SumVal %= Hou;
                }
                MatrixProd[LoopY, LoopX] = SumVal;
            }
        }
        return MatrixProd;
    }

    // 正方行列のP乗を、繰り返し2乗法で求める
    static long[,] DeriveBekijyouMatrix(long[,] pBaseMatrix, long pP)
    {
        long[,] CurrJyousuu = (long[,])pBaseMatrix.Clone();
        long CurrShisuu = 1;
        long[,] WillReturn = DeriveUnitMatrix(pBaseMatrix.GetUpperBound(0));

        while (true) {
            // 対象ビットが立っている場合
            if ((pP & CurrShisuu) > 0) {
                WillReturn = DeriveMatrixProd(WillReturn, CurrJyousuu);
            }

            CurrShisuu *= 2;
            if (CurrShisuu > pP) return WillReturn;
            CurrJyousuu = DeriveMatrixProd(CurrJyousuu, CurrJyousuu);
        }
    }

    // 単位行列を返す
    static long[,] DeriveUnitMatrix(long UB)
    {
        long[,] WillReturn = new long[UB + 1, UB + 1];
        for (long LoopY = 0; LoopY <= UB; LoopY++) {
            for (long LoopX = 0; LoopX <= UB; LoopX++) {
                if (LoopY == LoopX) {
                    WillReturn[LoopY, LoopX] = 1;
                }
            }
        }
        return WillReturn;
    }

    ////////////////////////////////////////////////////////////////
    // IEnumerable<string>をlongの2次元配列に設定する
    ////////////////////////////////////////////////////////////////
    static long[,] CreateBanArr(IEnumerable<string> pStrEnum)
    {
        var StrList = pStrEnum.ToList();
        if (StrList.Count == 0) {
            return new long[0, 0];
        }

        long[] IntArr = { };
        Action<string> SplitAct = pStr =>
            IntArr = pStr.Split(' ').Select(pX => long.Parse(pX)).ToArray();

        SplitAct(StrList[0]);

        long UB_X = IntArr.GetUpperBound(0);
        long UB_Y = StrList.Count - 1;

        long[,] WillReturn = new long[UB_X + 1, UB_Y + 1];

        for (long Y = 0; Y <= UB_Y; Y++) {
            SplitAct(StrList[(int)Y]);
            for (long X = 0; X <= UB_X; X++) {
                WillReturn[X, Y] = IntArr[X];
            }
        }
        return WillReturn;
    }
}


解説

最初に
同じフロアでの移動元と移動先の組み合わせごとの場合の数を
TSP問題として求めます。

考察すると
行列の積で、最終的な解が求まると分かります。

行列の積は、結合法則が成り立つので、最初と最後以外は、
繰り返し二乗法を使うを使ってます。


類題

Educational DP Contest R Walk