Derive Your Dreams

about me
Twitter: @kinaba

09:37 10/02/28

PrezzProcezz

TLEのPREP は、私のコードは縮める前がこんな感じで、

// println(X)
#define p(X) X _Pragma("")

// H = 百の位が…  (a: 3で割って0余る場合、 b: 1余る、 c: 2余る)
#define a(H) d(H 0) e(H 1) f(H 2) d(H 3) e(H 4) f(H 5) d(H 6) e(H 7) f(H 8) d(H 9)
#define b(H) e(H 0) f(H 1) d(H 2) e(H 3) f(H 4) d(H 5) e(H 6) f(H 7) d(H 8) e(H 9)
#define c(H) f(H 0) d(H 1) e(H 2) f(H 3) d(H 4) e(H 5) f(H 6) d(H 7) e(H 8) f(H 9)

// HJ = 百の位.十の位が…  (d: 3で割って0余る場合、 e: 1余る、 f: 2余る)
#define d(HJ) p(FizzBuzz) p(HJ 1) p(HJ 2) p(Fizz) p(HJ 4) p(Buzz) p(Fizz) p(HJ 7) p(HJ 8) p(Fizz)
#define e(HJ) p(Buzz) p(HJ 1) p(Fizz) p(HJ 3) p(HJ 4) p(FizzBuzz) p(HJ 6) p(HJ 7) p(Fizz) p(HJ 9)
#define f(HJ) p(Buzz) p(Fizz) p(HJ 2) p(HJ 3) p(Fizz) p(Buzz) p(HJ 6) p(Fizz) p(HJ 8) p(HJ 9)

// 1-9
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
// 10-99
e(1) f(2) d(3) e(4) f(5) d(6) e(7) f(8) d(9)
// 100-999
b(1) c(2) a(3) b(4) c(5) a(6) b(7) c(8) a(9)
// 1000
Buzz

基本的なアルゴリズムは普通だと思います。後は特に大きな工夫もなく地道なゴルフで、 最終的に提出した解が↓これ。

#define _ _Pragma("")
#define F _ Fizz
#define B _ Buzz
#define D(X)_ X 1 _ X 2 F _ X 4 B F _ X 7 _ X 8 F
#define d(X)F Buzz D(X)
#define e(X)B _ X 1 F _ X 3 _ X 4 F Buzz _ X 6 _ X 7 F _ X 9
#define f(X)B F _ X 2 _ X 3 F B _ X 6 F _ X 8 _ X 9
#define z(d,e,f,X)d(X 0)e(X 1)f(X 2)d(X 3)e(X 4)f(X 5)d(X 6)e(X 7)f(X 8)d(X 9)
D()e(1)f(2)d(3)e(4)f(5)d(6)e(7)f(8)d(9)z(e,f,d,1)z(f,d,e,2)z(d,e,f,3)z(e,f,d,4)z(f,d,e,5)z(d,e,f,6)z(e,f,d,7)z(f,d,e,8)z(d,e,f,9)B

とにかく #include __FILE__ を思いつけなかったのが敗因で、 そうすると、関数型マクロのみでどうにかするしかなくて、ただ、関数型マクロだと (1) 再帰ができない&条件分岐もできない (#if の分岐はマクロ定義の外でないと…) のでベタに全部出力するコードを書くしかない (2) 改行が出力できない…と思いきや、_Pragma(...) というものを挟むと、 これがなんなのかよくわかってないですが、出力に # で始まるコメント的な感じで埋まるので結果的に改行扱いになる! やった!といった雰囲気でした。

19:24 10/02/17

TLE'10

去年に引き続き Time Limit Exceeded というプログラミングコンテストに参加していました。そして去年に引き続き2位でした。昨年優勝の shinh さんが出題協力しているため本気で参加できないとかで、 これはもらった!と思っていたのに、別の方に完全敗北。トップの FancyMouse さん、 2問くらい手を付けてない問題を残して優勝です。すごいなあ。以下ネタバレ気味感想:

  ☆

⇒ 問題一覧。 使える処理系は gcc か g++ の 4.3 で、基本的にどの問題も、コードが短ければ短いほどスコアが高くなります)

PREP: これがshinhさんからの出題らしい。「FizzBuzzを1から1000まで出力してね、Cのプリプロセッサで」。 これは面白かったので問題紹介に留めておきます。みなさん挑戦してみてください。トップは172バイト。

PALIN2: 1≦n,p≦10万 程度の整数二つを与えたらnのn乗をpで割った余りを計算するプログラムを書いてね。 掛け算すると 32bit の int をギリギリ溢れるくらいの問題サイズなので、それだけ気をつけたらあとは実装するだけ。 ただしコードは同じ文字列を3回くり返した形 xxx でないと正解と見なしません、という条件があるんですが、 普通のコードを // で終わらせて3個並べるだけ。

PALIN1: PALIN2と同じ、ただしn,p≦10^18。掛け算すると64bit整数もあふれます。 仕方ないのであふれない掛け算を筆算的に実装。10^18+10^18 は溢れないので、 二進数表現での筆算はちょくちょくpで割ってあげれば溢れません。 PALIN2 より圧倒的に難しいと思うんですが、なんで点数低いんだろう。

CARM: カーマイケル数をできるだけ沢山出力するプログラムを書いてね、4096バイト以内で。 コードサイズではなく、出力したカーマイケル数の個数でスコアが決まります。 wikipedia に書いてある式を使って適当に実装して提出。 上位陣は4096バイトを上手く使って表でも埋め込んで置くのかなー、と思ったら違ったみたい。 結構マジメに計算している。すごい。

COMP: 巨大なアスキーアートを出力するプログラムを書いてね。 適当にランレングス圧縮したものを文字列リテラルとして持って、展開しながら出力するコードを提出、 というのは全員同じ方針だと思いますが、自分のは符号化方式がだいぶ甘かった。

SHORTEN: 指定されたコードと同じ動作をするできるだけ短いコードを書いてね。 今年のお題は、解読してみると要するに、「'(' と ')' の並んだ文字列が来るので、 括弧の対応がちゃんと取れているか判定するコード」。ただし、 「i番目の括弧の向きを反転」という命令と「今対応が取れてるかYES/NOで出力」という命令が次々来るので、 それに答えられるように作る、という感じ。 元のコードは括弧の向きの反転を高速化するためにツリー型のインデックスを作っているんだけど、 そこまでしなくても毎回全部文字列見るので十分でした。あとはひたすらゴルフ。

KEY: 予約語1個につき50文字、セミコロン1個につき10文字、空白1個につき5文字、 のペナルティつきゴルフ。forループを再帰に変えて、セミコロンを減らすために関数を融合させたら(予約語なしだと、 関数1個につきセミコロン1個は最低必要になるので、減らせるなら減らしたい)あとは普通にゴルフ。 面白いと思うんですけど、面倒くさいので解く人が少ないのが悲しい…。

CQUINE: プログラムAはBを出力し、BはAを出力する、というタイプのQuine。プラスアルファ。 Quineは苦手だ…という問題ではなく、普通にゴルフ力で負けた気がする。

  ☆

全体的に、終了後に他の人のソースを見て思ったのは、自分は #define のことを完全に忘れていたなあ、と。 Cゴルフにおいて「本当に本当の最適解に #define が残ることはあるか」 というのは議論の残るところだと認識しているんですが、 TLE のような(他のゴルフコンペと比べて)短期間で多数の問題を解くような場合であれば、 とりあえずの水準の答えをサクッと作る時には #define はアリっぽい。

presented by k.inaba (kiki .a.t. kmonos.net) under CC0