sasaharayuugo.net

ジュニア算数オリンピック 三次元配列の問題 その3

【随時更新】解答に必要な機能まとめ - ニート歴10年からの数学日記【随時更新】計算量の減らし方まとめ - ニート歴10年からの数学日記を使って、ジュニア算数オリンピックの三次元配列の問題について考察していく。
 

 

10年度トライアル問題 問題12

『図1の展開図を組み立てて10個のサイコロを作り、それらを下のルールにしたがって図2のような立体を作りました。

ルール:重なっている2つの面の和は「3」か「9」です。

いま、面ABCD、面EFGH、面LIJKに書かれている数字が2、3、1であることがわかっています(ただし数字の向きはわかりません)。
このとき、面IMNJに書かれていいる数字を解答らんに正しい向きで書き入れなさい。

(図1)

普通の1右回転の2逆さの3
左回転の4普通の5右回転の6

(図2 フレミングの法則のように立方体が積み重なっていて、真ん中の1つからそれぞれ3つ伸びている。上の上面がABCDで、左端の端面がEFGH、右端の上面がIJKL、右端のこちら向きがIMNJ)』

dice := ["+y" : 1, "-y" : 3, "+x" : 2, "-x" : 5, "+z" : 4, "-z" : 6];
    ,
    def diceroll_axis_x(dice) {
        for(i in range(?)) {
            dice["+y"] := dice["+z"];
            dice["-y"] := dice["-z"];
            dice["+z"] := dice["-y"];
            dice["-z"] := dice["+y"];
        }
    }
    def diceroll_axis_y(dice) {
        for(i in range(?)) {
            dice["+x"] := dice["+z"];
            dice["-x"] := dice["-z"];
            dice["+z"] := dice["-x"];
            dice["-z"] := dice["+x"];
        }
    }
    def diceroll_axis_z(dice) {
        for(i in range(?)) {
            dice["+y"] := dice["+x"];
            dice["-y"] := dice["-x"];
            dice["+x"] := dice["-y"];
            dice["-x"] := dice["+y"];
        }
    }
    ,
    table := [
        [
            ["D",,,],
            [,,,],
            [,,,],
            [,,,]
        ]
        ,
        [
            ["D",,,],
            [,,,],
            [,,,],
            [,,,]
        ]
        ,
        [
            ["D",,,],
            [,,,],
            [,,,],
            [,,,]
        ]
        ,
        [
            ["D", "D", "D", "D"],
            ["D",,,],
            ["D",,,],
            ["D",,,]
        ]
    ]

    for(y in range(4)) {
        for(x in range(4)) {
            for(z in range(4)) {
                if(table[y][x][z] == "D") {
                    table[y][x][z] := copy(dice);

                    table[y][x][z] := diceroll_axis_y(table[y][x][z]);

                    table[y][x][z] := diceroll_axis_x(table[y][x][z]);

                    table[y][x][z] := diceroll_axis_z(table[y][x][z]);
                }
            }
        }
    }

    every(y) {
        or(1) {
            table[y][0][0]["+y"] + table[y + 1][0][0]["-y"] == 3;
        } or {
            table[y][0][0]["+y"] + table[y + 1][0][0]["-y"] == 9;
        }
    }
    every(x) {
        or(1) {
            table[3][x][0]["+x"] + table[3][x + 1][0]["-x"] == 3;
        } or {
            table[3][x][0]["+x"] + table[3][x + 1][0]["-x"] == 9;
        }
    }
    every(z) {
        or(1) {
            table[3][0][z]["+z"] + table[3][0][z + 1]["-z"] == 3;
        } or {
            table[3][0][z]["+z"] + table[3][0][z + 1]["-z"] == 9;
        }
    }

    table[0][0][0]["-y"] == 2;
    table[3][3][0]["+x"] == 3;
    table[3][0][3]["+z"] == 1;

    print(table[3][0][3]["+x"]);


二種類あるダイスを区別しないと、真ん中のダイスが決まらなくて解けない問題で、そこに苦戦した。
今振り返れば、わざわざ回さなくても、例えばそれぞれのダイスにおいてy軸とx軸とz軸の回転で取り出して、循環リストでオリジナルの順番と、3つがどれか3つとマッチすれば良いという制約にしても良かったかもしれない。


実際、解き方としても、既に決まっている(つまり制約が強い)側面から、ダイスの制約で辿っていって、答えに至る。オリジナルのダイスを回してなんかいない。
 

10年度ファイナル問題 問題6

『図のような1辺が1cmの立方体が8個あります。この立方体は、図のように3面が黒色で、残りの3面が白色になっています。

(図 それぞれ辺を共有した立方体の黒い3面の図)

いま、この立方体を同じ色の面同士がつながるようにして1辺が2cmの立方体を作ろうと思います。このとき、そのつなげ方は何通りあるでしょうか。ただし、回転すると同じになるものもそれぞれ1通りとしてかぞえることとします。』

table := [~1][~1][~1];

    table[?][?][?] := カブり無しで全部に : ? : カブり有り := ["+y": None, "-y": None, "+x": None, "-x": None, "+z": None, "-z": None];

    for(y in range(2)) {
        for(x in range(2)) {
            for(z in range(2)) {
                or(1) { table[y][x][z]["+y"] := "白"; table[y][x][z]["-y"] := "黒"; } or { table[y][x][z]["+y"] := "白"; table[y][x][z]["-y"] := "黒"; }
                or(1) { table[y][x][z]["+x"] := "白"; table[y][x][z]["-x"] := "黒"; } or { table[y][x][z]["+x"] := "白"; table[y][x][z]["-x"] := "黒"; }
                or(1) { table[y][x][z]["+z"] := "白"; table[y][x][z]["-z"] := "黒"; } or { table[y][x][z]["+z"] := "白"; table[y][x][z]["-z"] := "黒"; }
            }
        }
    }

    every(y, x) {
        table[y][x][0]["+z"] == table[y][x][1]["-z"];
    }
    every(y, z) {
        table[y][0][z]["+x"] == table[y][1][z]["-x"];
    }
    every(x, z) {
        table[0][x][z]["+y"] == table[1][x][z]["-y"];
    }

    よく分からないのでここで中断


回転の処理が分からなかったので答えを見た。
しかし、どうも書いてないというか、要するに大きな3面だけ決めれば制約によって残りの3面も決まる。2^4 * 2^4 * 2^4=4096通りが答えだそうだ。
 

11年度トライアル問題 問題11

『次のような3面が黒くぬられた1辺が1cmの2種類の立方体の展開図があります。展開図を組み立てたときにできる立方体をそれぞれA、Bとします。

(図 それぞれ辺を共有した3面が組み合うような立方体と、「コ」の字が組み合うような立方体。それぞれ白と黒の対になっている)

いま、これらの立方体を同じ色の面がつながるようにはりつけて、下の図のような1つの頂点の周りの3面が黒く、それ以外の面は色がぬられていない、一辺が2cmの立方体を作りたいと思います。この時、A、Bはそれぞれ何個用いればよいですか。

(図は省略 説明文通り)』

table := [~1][~1][~1];

    table[?][?][?] := カブり無しで全部に : ? : カブり有り := ["+y": None, "-y": None, "+x": None, "-x": None, "+z": None, "-z": None];
    A_count := 0;
    B_count := 0;

    every(y, x, z) {
        or(1) {
            A_count := A_count + 1;
            ,
            or(1) { table[y][x][z]["+y"] := "白"; table[y][x][z]["-y"] := "黒"; } or { table[y][x][z]["+y"] := "白"; table[y][x][z]["-y"] := "黒"; }
            or(1) { table[y][x][z]["+x"] := "白"; table[y][x][z]["-x"] := "黒"; } or { table[y][x][z]["+x"] := "白"; table[y][x][z]["-x"] := "黒"; }
            or(1) { table[y][x][z]["+z"] := "白"; table[y][x][z]["-z"] := "黒"; } or { table[y][x][z]["+z"] := "白"; table[y][x][z]["-z"] := "黒"; }
        } or {
            B_count := B_count + 1;
            ,
            or(1) {
                table[y][x][z]["+y"] := "黒";
                table[y][x][z]["-y"] := "黒";
                or(1) { table[y][x][z]["+x"] := "黒"; } or { table[y][x][z]["-x"] := "黒"; } or { table[y][x][z]["+z"] := "黒"; } or { table[y][x][z]["-z"] := "黒"; }

                table[y][x][z][?].(it != "黒") := "白";
            } or {
                table[y][x][z]["+x"] := "黒";
                table[y][x][z]["-x"] := "黒";
                or(1) { table[y][x][z]["+y"] := "黒"; } or { table[y][x][z]["-y"] := "黒"; } or { table[y][x][z]["+z"] := "黒"; } or { table[y][x][z]["-z"] := "黒"; }

                table[y][x][z][?].(it != "黒") := "白";
            } or {
                table[y][x][z]["+z"] := "黒";
                table[y][x][z]["-z"] := "黒";
                or(1) { table[y][x][z]["+y"] := "黒"; } or { table[y][x][z]["-y"] := "黒"; } or { table[y][x][z]["+x"] := "黒"; } or { table[y][x][z]["-x"] := "黒"; }

                table[y][x][z][?].(it != "黒") := "白";
            }
        }
    }

    every(y, x) {
        table[y][x][0]["+z"] == table[y][x][1]["-z"];
    }
    every(y, z) {
        table[y][0][z]["+x"] == table[y][1][z]["-x"];
    }
    every(x, z) {
        table[0][x][z]["+y"] == table[1][x][z]["-y"];
    }

    every(y, x) {
        table[y][x][1]["+z"] == "黒";
    }
    every(y, z) {
        table[y][1][z]["+x"] == "黒";
    }
    every(x, z) {
        table[1][x][z]["+y"] == "黒";
    }

    print(A_count, B_count);


この「,」による見た目だけの改行っていうのはどうなんだろう、特に意味論的にどうなんだろうというのは思う。
例えば「{文1; 文2; 文3;}」という風に書いたりするわけだけど、「{文1; , 文2; , 文3;}」なのか、と。
じゃあと思って「#」を挟んでみてもウザったいし。何か良い方法は無いものかな。
今思いついた「''''''」は有力かもしれない。こういうのってpythonだと内部的にはどういう処理になるんだろう。

って、ああそうかpythonは改行で一文が途切れたか。どうするかな、本当に。


解答は、まあ黒の角から何となく考えていけば出るんじゃないか。
 

11年度ファイナル問題 問題6

『2cm×2cm×5cmでふたのついた立方体の箱の中に、2cm×1cm×1cmの積み木を10個入れてふたをしめたいと思います。このとき、積み木の入れ方は全部で何通りありますか。』

table := [~1][~1][~4];

    for(i in range(10)) {
        or(1) {
            table[?1][?2][?3], table[?1 + 1][?2][?3] == False;

            table[?1][?2][?3], table[?1 + 1][?2][?3] := i;
        } or {
            table[?1][?2][?3], table[?1][?2 + 1][?3] == False;

            table[?1][?2][?3], table[?1][?2 + 1][?3] := i;
        } or {
            table[?1][?2][?3], table[?1][?2][?3 + 1] == False;

            table[?1][?2][?3], table[?1][?2][?3 + 1] := i;
        }
    }

    ここでtableの0~9で数字を交換しただけのものを消去する

    print(table.数);


解き方は、分からないなあ。まず10個の積み木はピッタリ入る。
5列ある内の1列だけに納まる場合を考えると、縦縦か横横で2通り。
2列に納まる場合を考えると、全部揃えるので3通り(いや、1列の場合を引くと1通りだった)、2個2個でズラすので6通り(4通りだった)。
3列は、2個2個で真ん中1列共有するような、組み合う形で、これは残りの2個に着目すると分かりやすいけど、意外に4通り。
こう法則を発見したのだけど、複数列にまたがるように横方向に置くと、その下か横?に揃えるようにもう一つ置く必要がある。それぞれの列で完結しないようにそれを続けると、同じようなパターンに落ち着く。4列も4通りで、5列も4通りだろう。

組み合わせを考えると、5、4と1、3と2、3と1と1、2と2と1、2と1と1と1、1と1と1と1と1。
5は4通り。
4と1は、4*2=8を、4と1の場合、1と4の場合で、*2して=16通り。
3と2は、5*4=20を、同じく2パターンで、=40通り。
3と1と1は、3の位置で3パターンで〜、と。
まあ、こんな感じで良いだろう。最後に全部を足し合わせれば、それが答え。