トップページに戻る    次の増井さんの書籍の問題へ    前の増井さんの書籍の問題へ

Q68 隣り合わないのがマナー


C#のソース

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

class Program
{
    static void Main()
    {
        //場合の数[座席の状態]なDP表
        var PrevDP = new Dictionary<string, int>();
        PrevDP["*SSSSSS*SSSSSS*"] = 1;

        int AnswerCnt = 0;
        int DPCnt = 0;
        while (PrevDP.Count > 0) {
            var CurrDP = new Dictionary<string, int>();
            foreach (var EachPair in PrevDP) {
                bool HasRyoudonariSpace = false;
                for (int I = 0; I <= EachPair.Key.Length - 1; I++) {
                    //両隣が空席な、空席の場合
                    if (IsRyoudonariSpace(EachPair.Key, I)) {
                        HasRyoudonariSpace = true;
                        string NewKey = EachPair.Key;
                        NewKey = NewKey.Remove(I, 1).Insert(I, "F");

                        if (CurrDP.ContainsKey(NewKey))
                            CurrDP[NewKey] += EachPair.Value;
                        else CurrDP[NewKey] = EachPair.Value;
                    }
                }

                //両隣が空席な、空席が1つもなかった場合
                if (HasRyoudonariSpace == false) {
                    int SpaceCnt = EachPair.Key.Count(X => X == 'S');
                    //積の法則で求めた場合の数を、和の法則で計上
                    AnswerCnt += EachPair.Value * DeriveKaijyou(SpaceCnt);
                }
            }
            Console.WriteLine("{0}回目のDPの結果", ++DPCnt);
            PrintDP(CurrDP);
            PrevDP = CurrDP;
        }
        Console.WriteLine("答えは{0}通り", AnswerCnt);
    }

    //両隣が空席な、空席かを返す
    static bool IsRyoudonariSpace(string pJyoutaiStr, int pCurrInd)
    {
        if (pJyoutaiStr[pCurrInd] != 'S') return false;
        if (pJyoutaiStr[pCurrInd - 1] == 'F') return false;
        if (pJyoutaiStr[pCurrInd + 1] == 'F') return false;
        return true;
    }

    //階乗を求める
    static int DeriveKaijyou(int pTarget)
    {
        int WillReturn = 1;
        for (int I = 2; I <= pTarget; I++)
            WillReturn *= I;
        return WillReturn;
    }

    static void PrintDP(Dictionary<string, int> pDPDict)
    {
        foreach (var EachPair in pDPDict) {
            Console.WriteLine("{0}の場合の数={1}",
                EachPair.Key, EachPair.Value);
        }
    }
}


実行結果

省略
6回目のDPの結果
*FSFSFS*FSFSFS*の場合の数=720
*FSFSFS*FSFSSF*の場合の数=720
*FSFSFS*FSSFSF*の場合の数=720
*FSFSFS*SFSFSF*の場合の数=720
*FSFSSF*FSFSFS*の場合の数=720
*FSFSSF*FSFSSF*の場合の数=720
*FSFSSF*FSSFSF*の場合の数=720
*FSFSSF*SFSFSF*の場合の数=720
*FSSFSF*FSFSFS*の場合の数=720
*FSSFSF*FSFSSF*の場合の数=720
*FSSFSF*FSSFSF*の場合の数=720
*FSSFSF*SFSFSF*の場合の数=720
*SFSFSF*FSFSFS*の場合の数=720
*SFSFSF*FSFSSF*の場合の数=720
*SFSFSF*FSSFSF*の場合の数=720
*SFSFSF*SFSFSF*の場合の数=720
7回目のDPの結果
答えは14100480通り


解説

和の法則や積の法則で、場合の数を更新する動的計画法を使ってます。