トップページに戻る    次の競技プログラミングの問題へ    前の競技プログラミングの問題へ

結城さん問題01 マヨイドーロ

■■■マヨイドーロ■■■

以下の道をマヨイドーロと呼びます。



Xがマヨイドーロの入口で、YとZが出口になります。
A,B,Cをマヨイと呼びます。
マヨイは「そのまま直進してもいいけれど、進む向きを反転してもいい地点」を表します。

入口Xからマヨイドーロに入った人は、最初は必ずZ向きに進み、Bに着きます。
その後、Xへ戻ることはできず、マヨイドーロの中を通過して、YかZの出口から出ます。

たとえばアリスは、マヨイドーロをこんなふうに通りました。
●Xから入り、Bに着いた。
●反転してYに向かい、Aに着いた。
●反転してZに向かい、Bに着いた。
●直進してZに向かい、Cに着いた。
●直進してZから出た。
つまりアリスは、反転を2回行って、
X→B→A→B→C→Z
というルートを通ったことになります。

またボブは、マヨイドーロをこんなふうに通りました。
●Xから入り、Bに着いた。
●直進してZに向かい、Cに着いた。
●反転してYに向かい、Bに着いた。
●直進してYに向かい、Aに着いた。
●直進してYから出た。
つまりボブは、反転を1回行って、
X→B→C→B→A→Y
というルートを通ったことになります。

■■■NとP■■■

Nを「反転回数の上限」とします。
言い換えるならNは「その回数までは反転してかまわないという数」です。
Pを「Xから入ってYから出るルートの種類の数」とします。

N=0の例
もしもNが0の場合には、直進しかできませんので、
●X→B→C→Z
という、Zから出るルートしかありません。したがって、Yから出るルートの総数Pは、0になります。

N=1の例
もしもNが1の場合、Yから出るルートは、
●X→B→C→B→A→Y
●X→B→A→Y
という2種類があるので、Pは2になります。

N=4の例
もしもNが4の場合、Yから出るルートは、
●X→B→C→B→A→Y
●X→B→C→B→A→B→C→B→A→Y
●X→B→C→B→A→B→A→Y
●X→B→C→B→C→B→A→Y
●X→B→A→Y
●X→B→A→B→C→B→A→Y
●X→B→A→B→A→Y
という7種類があるので、Pは7になります。

■■■問題■■■

Nが与えられたとき、Pを出力するプログラムを書いてください。


C++のソース

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>

std::string InputPattern = "Input1";

std::vector<std::string> GetInputValues()
{
    std::vector<std::string> InputValuesVect;

    if (InputPattern == "Input1") {
        InputValuesVect.push_back("1");
        //2
    }
    else if (InputPattern == "Input2") {
        InputValuesVect.push_back("4");
        //7
    }
    else { //実際の入力
        while (true) {
            std::string wkStr;
            std::getline(std::cin, wkStr);
            if (std::cin.eof()) break;
            InputValuesVect.push_back(wkStr.c_str());
        }
    }
    return InputValuesVect;
}

class CppBigInteger{
    static const int UB = 450;
    int mNumArr[UB + 1];

public:
    CppBigInteger()
    {
        for (int I = 0; I <= UB; I++) {
            mNumArr[I] = 0;
        }
    }

    void Add(int pNum)
    {
        int CurrInd = 0;
        while (pNum > 0) {
            mNumArr[CurrInd++] += pNum % 10;
            pNum /= 10;
        }
        KetaAgariSyori();
    }

    void Add(CppBigInteger pIns)
    {
        for (int I = 0; I <= UB; I++) {
            mNumArr[I] += pIns.mNumArr[I];
        }
        KetaAgariSyori();
    }

    //文字列で表現
    std::string DeriveStr()
    {
        std::string WillOut;
        bool FoundNonZero = false;
        for (int I = UB; 0 <= I; I--) {
            if (mNumArr[I] != 0) FoundNonZero = true;
            if (FoundNonZero){
                char wkChar[100] = "\0";
                sprintf(wkChar, "%d", mNumArr[I]);
                WillOut += wkChar;
            }
        }
        if (FoundNonZero == false) return "0";
        return WillOut;
    }

private:
    //桁上がりを処理
    void KetaAgariSyori()
    {
        for (int I = 0; I <= UB; I++) {
            if (mNumArr[I] >= 10) {
                mNumArr[I + 1] += mNumArr[I] / 10;
                mNumArr[I] %= 10;
            }
        }
    }
};

int main()
{
    std::vector<std::string> InputVect = GetInputValues();
    int N = atoi(InputVect.at(0).c_str());
    std::vector<CppBigInteger> PatternCntVect;

    CppBigInteger Kou1; Kou1.Add(1);
    PatternCntVect.push_back(Kou1);

    CppBigInteger Kou2; Kou2.Add(2);
    PatternCntVect.push_back(Kou2);

    for (int I = 2; I <= N; I++) {
        CppBigInteger wkKou;
        wkKou.Add(PatternCntVect.at(I - 2));
        wkKou.Add(PatternCntVect.at(I - 1));
        PatternCntVect.push_back(wkKou);

        //printf("%d回反転して、出口に到達する場合の数は%s\n",
        //  I, PatternCntVect.at(I).DeriveStr().c_str());
    }
    CppBigInteger PatternCntSum;
    for (int I = 1; I <= N; I += 2) {
        PatternCntSum.Add(PatternCntVect.at(I));
    }
    printf("%s\n", PatternCntSum.DeriveStr().c_str());
}


解説

下記の考えによる漸化式を使ってます。

0回反転で最初の向きの出口に到達は1通り
1回反転で最初の逆向きの出口に到達は2通り

n回 (N >= 2 ) 反転で出口に到達は、
最初に反転したら、次は反転しかないので、
n-2回反転で最初の向きの出口に到達する場合の数
最初に反転しなかったら、次は反転しかないので、
n-1回反転で最初の逆向きの出口に到達する場合の数