カメヲラボ

主にプログラミングとお勉強全般について書いてます

チョー短いコードを通しといたわん

0257 - Railway Ticket

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=0257
問題文は省略して、どんなコードを書けばよいかというと、
(半角スペースを含んだ)7文字の入力に対して、

入力 投入状態 投入に対する扉の動作
1 0 0 「乗車券」のみ投入 Close
0 1 0 特急券」のみ投入 Close
1 1 0 「乗車券」と「特急券」投入 Open
0 0 1 「乗車・特急券」投入 Open
0 0 0 投入なし Close

上記のルールでOpenまたはCloseと出力するだけの問題。

先日、CodeIQのショートコーディングで見事1位になったclimpetさんのブログを見ていて、ピピンと来たので縮めてみました。

Cで50B

1行をまとめてgetsで読み込んでしまうテクニックを使って、2つのグローバル変数に値を割り当てます。
32bit整数には、4文字分が読み込まれるので、たとえば"1 0 0"という入力であれば、1つ目の変数には' 0 1'=540024881、2つ目の変数には'0'=48がセットされます。リトルエンディアンなので並びがちょっと変わります。これらの2つの値を適当にごにょごにょして、最終的に0か1に変換する式を書けば、

puts(ごにょごにょ?"Open":"Close")

みたいに短く書けます。ということで、ごにょごにょした結果

j,k;main(){gets(&j);j=!puts(j%k%3?"Close":"Open");}

という感じで51Bになりました。変数名をj,kとしてありますが、変数名によってはメモリ上の配置がうまく合わず、何でも良いわけではありません。あと、AOJの場合は0を返さないとイカンらしいので、無駄にjに0を代入する式があります。これは仕方ないみたいですね。

追記
代入の部分をいじってみたら、こんなんが通りました。

j,k;main(){j=gets(&j)&puts(j%k%3?"Close":"Open");}

ちょっと怪しげですね…。

Rubyで26B

ちなみにRubyで26Bのコードを通していますが、これはインチキです。インチキではありますが、これは入念に調査しておかないと通すのは不可能です。Rubyでは、プロセスIDを利用します。プロセスIDは乱数とは違うので、規則性があり、デタラメにやっても通りません。ちゃんと通る確率のあるコードを書かなければなりませんので、テストケースを解析します。解析方法は…省略。で、この問題はテストケースが6つありまして、正解は順に

Open
Close
Close
Open
Open
Close

になっています。また、RubyのコードをSubmitする場合、テスト毎にPIDが16ずつ進むみたい(解析方法は省略)なので、16の倍数に対してある演算を行い、結果が

1, 0, 0, 1, 1, 0

となる式を検討します。

16, 32, 48, 64, 80, 96, 112...

に対して、7での剰余を求めると、

0, 2, 4, 6, 1, 3, 5...

となります。これが周期的に繰り返すので、このパターンから、先程の01の並びを作ることができればOKです。
7での剰余を2進表記すると

000
010
100
110
001
011
101

のようになりますが、一番左側のビットを見ると、

0011001

になっています。これはループするので、もう1周期つないでみると、

00110010011001

100110のパターンがありました!
これを利用して、

puts$$%7/4>0?:Open:"Close"

のように書けば、何回かSubmitすれば通ります。乱数でやるには2^6分の1の確率で、まあ現実的な値ではありますがちょっと大変ですね。あと、細かいテクニックとして、文字列"Open"の代わりに:Openのようにシンボル名にしています。putsとかprintでは普通にOpenと表示されます。