コードを短くするのって楽しいですよね?(6)

昨日の続き。まずは、Inputが何種類あるのかを確定させる。


異なるInputでは、最初の数字が異なると仮定すれば、まずは最初の数字をscanfで読み込むところから始まる。最初の数字をnとすれば


if (n>=30) return ;

のようにして、ひとつ目の数字の範囲を調べていく。30ではaccept。すなわち、ひとつ目の数字は30以上ではない。20でWA。25でaccept。22でWA。23でaccept。よって、ひとつ目の数字のうち、ひとつは22。残りは22以下。トラップを変更。


if (n!=22 && n>=15) return ;

15で判定。これaccept。よって、残りの数字は15未満。こうやって調べていくと、もうひとつの数字が10であることが判明。この二つしか無いのかどうかを調べる。


if (n!=22 && n!=10) return ;

これがaccept。よって、Inputは複数あるかも知れないが、ひとつ目の数字は2種類しかないことが判明。Inputも2種類かも知れない。


次に、ひとつ目の数字が22の場合と10の場合について、それぞれテストケースの数を調査。後者のテストケースの数が17で、前者は4であることが判明。そして、真偽値を確定させていく。


yynynnyyyyynyyyny // ひとつ目が10
ynyn // ひとつ目が22

こうなった。2-4番目のテストケースに対するanswerは、前者と後者とで異なることはわかっていたから、その部分で真偽値が逆になっているのは当然だろう。これをbit列で(ひとつ目に対するanswerがbit0に来るように)書くと


10111011111001011b == 96203
00000000000000101b == 9

である。一文字目をgetcharした場合、10か22ならば、'1'(49)か'2'(50)が得られることはわかっているから、getchar()&1で場合わけして、上の定数をシフトしながらループをまわす。たとえば次のようなコードが考えられる。


for(c=getchar()&1?96203:9;c/=2;)puts(c&1?"yes","no");

しかし、不運なことに、ひとつ目の数字が22の場合は最後がnoで終わっているため、このループ脱出条件は正しくない。“番人”をどこかに置く必要がある。


110111011111001011b == 227275 (最上位bitに1を追加)
000000000000010101b == 21 (bit4に1を追加)

として、


main(d){for(d=getchar()&1?227275:21;d-1;d/=2)puts(d&1?"yes":"no");}

こうだろうか?あまりスマートなコードではない。getcharの返し値を生かしきれていないし、d/=2とd-1と二箇所にわかれているのは冗長だと感じる。ビット&演算子のほうが'+'より優先順位が高ければ、


puts("yes\0no"+d&4)
のようにして(この場合、定数も4倍しておく必要がある。またループ判定条件も d-1 ではなくd/4 と変更する必要がある)1文字短縮することが出来るのだが。


あるいは、"/="のほうが'-'より優先順位が高ければ、条件判定を d/=2-1と書いたり、あるいは、"/="の左が左辺値(lvalue)であるという制限が緩和されていれば、条件判定を 1-d/=2 のように書くことが出来るのだが。


getcharをgetchに書き換えてみたがダメ。getchar()&1をrand()%2に書き換えてみたけど、乱数はsrandを使わない限り同じ系列の乱数が与えられるのでダメ。乱数という発想を生かすならpid()&1は、面白いけどincludeがいるのでダメ。time(0)&1で、これがacceptされれば2バイト縮まって、tanakhさんのと同じ65byteになるのだけど、2回のInputは連続してやってくるので、このタイミングで秒の境界をまたぐのは至難の業。もうひとつアカウントをとって、永久ループとかでサーバーを負荷状態にするものをcommitした直後にこちらをcommitしようかと思ったが、どうやらcommitしたものは処理時間の算出のために処理待ちのqueueのなかに入れられて、ひとつずつ実行されているようである。よって、この案も没。


結局どうやって65byteにしているのかわからない。これがtanakhさんの言う“新機軸”なのか。せっかくのブレークスルーもここに来て完敗である。ひょっとして、コマンドラインの引数で判別か?(つづく)