トップページに戻る    次のPL/SQLの問題へ    前のPL/SQLの問題へ    Java版

PL/SQL6 川渡り 農夫と狼とヤギとキャベツ

SQLパズル

この問題は、8世紀にカンタベリーの大主教が提示した問題といわれている。

オオカミとヤギを連れキャベツを持った農夫が川岸にいる。
川にはボートがあるが農夫の他には動物一頭かキャベツ一個しか乗せられない。
農夫がいなければオオカミはヤギを襲うし、ヤギはキャベツを食べてしまう。

すべてを無事に対岸に渡すにはどうしたらよいか?


ソース

declare
    type InfoDef is record(
        pNouhu   pls_Integer,
        pOOkami  pls_Integer,
        pYagi    pls_Integer,
        pKyabetu pls_Integer,

        LogPath varchar2(2000),
        LV pls_Integer
    );

    stackP pls_Integer := 0;
    type StackClass is table of InfoDef index by binary_integer;
    st StackClass;
    priInfo  InfoDef;
    willPush InfoDef;
    LoopChk pls_Integer :=0;
    willWrite varchar2(200);
    ansCnt pls_Integer := 0;

    procedure push(pInfoDef InfoDef) is
    begin
        willWrite := '(';
        if (pInfoDef.pNouhu   = 0) then willWrite := willWrite || '人'; end if;
        if (pInfoDef.pOOkami  = 0) then willWrite := willWrite || '狼'; end if;
        if (pInfoDef.pYagi    = 0) then willWrite := willWrite || 'ヤ'; end if;
        if (pInfoDef.pKyabetu = 0) then willWrite := willWrite || 'キ'; end if;
        willWrite := willWrite || '■';
        if (pInfoDef.pNouhu   = 1) then willWrite := willWrite || '人'; end if;
        if (pInfoDef.pOOkami  = 1) then willWrite := willWrite || '狼'; end if;
        if (pInfoDef.pYagi    = 1) then willWrite := willWrite || 'ヤ'; end if;
        if (pInfoDef.pKyabetu = 1) then willWrite := willWrite || 'キ'; end if;
        willWrite := willWrite || ')';

        st(stackP) := pInfoDef;
        st(stackP).LogPath := st(stackP).LogPath || willWrite;
        stackP := stackP+1;
    end;

    function pop return InfoDef is
    begin
        stackP := stackP-1;
        return st(stackP);
    end;

    function isEmpty return boolean is
    begin
        return stackP = 0;
    end;

    function isValid(pInfoDef InfoDef) return boolean is
    begin
        if ((pInfoDef.pNouhu = 0 and pInfoDef.pOOkami = 1 and pInfoDef.pYagi = 1) or
            (pInfoDef.pNouhu = 1 and pInfoDef.pOOkami = 0 and pInfoDef.pYagi = 0) or
            (pInfoDef.pNouhu = 0 and pInfoDef.pYagi   = 1 and pInfoDef.pKyabetu = 1) or
            (pInfoDef.pNouhu = 1 and pInfoDef.pYagi   = 0 and pInfoDef.pKyabetu = 0)) then
            return false;
        end if;
        return true;
    end;

begin
    --Start With句
    willPush.LV := 1;
    willPush.pNouhu := 1;
    willPush.LogPath := null;

    willPush.pOOkami := 0; willPush.pYagi := 0;willPush.pKyabetu := 1; push(willPush);
    willPush.pOOkami := 0; willPush.pYagi := 1;willPush.pKyabetu := 0; push(willPush);
    willPush.pOOkami := 1; willPush.pYagi := 0;willPush.pKyabetu := 0; push(willPush);

    ansCnt := 0;
    while (isEmpty = false) loop
        priInfo := pop();

        -- 合格経路なら表示
        if (priInfo.pNouhu   = 1 and
            priInfo.pOOkami  = 1 and
            priInfo.pYagi    = 1 and
            priInfo.pKyabetu = 1) then
            ansCnt := ansCnt+1;
            DBMS_Output.Put_Line('解' || to_char(ansCnt));
            DBMS_Output.Put_Line(priInfo.LogPath);
        end if;

        priInfo.LV := priInfo.LV+1;

        if (priInfo.LV < 6) then
            if (priInfo.pNouhu = 0) then
                priInfo.pNouhu := 1;
                if (isValid(priInfo)) then push(priInfo); end if;

                if (priInfo.pOOkami = 0) then
                    priInfo.pOOkami := 1;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pOOkami := 0;
                end if;
                if (priInfo.pYagi = 0) then
                    priInfo.pYagi := 1;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pYagi := 0;
                end if;
                if (priInfo.pKyabetu = 0) then
                    priInfo.pKyabetu := 1;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pKyabetu := 0;
                end if;
            else
                priInfo.pNouhu := 0;
                if (isValid(priInfo)) then push(priInfo); end if;

                if (priInfo.pOOkami = 1) then
                    priInfo.pOOkami := 0;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pOOkami := 1;
                end if;
                if (priInfo.pYagi = 1) then
                    priInfo.pYagi := 0;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pYagi := 1;
                end if;
                if (priInfo.pKyabetu = 1) then
                    priInfo.pKyabetu := 0;
                    if (isValid(priInfo)) then push(priInfo); end if;
                    priInfo.pKyabetu := 1;
                end if;
            end if;
        end if;
    end Loop;
end;
/


実行結果

解1
(ヤキ■人狼)(人ヤキ■狼)(ヤ■人狼キ)(人ヤ■狼キ)(■人狼ヤキ)
解2
(狼ヤ■人キ)(人狼ヤ■キ)(ヤ■人狼キ)(人ヤ■狼キ)(■人狼ヤキ)


解説

Oracleの階層問い合わせを意識した、深さ優先探索を使ってます。