カメヲラボ

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

最短コード88B

等差数列・等比数列の判別と言えば、
任意の3項(a,b,c)について


等差:b+b=a+c
等比:b*b=a*c
が成り立つことは一般的に知られているので、当然のようにこれを使う。当然のようにというより、この関係式があまりに一般的過ぎてこれしか思いつかないというのが実際のところだ。


しかし、この問題は4項目まで与えられていて5項目を答える問題。しかもその値はすべて自然数である。私はまともに問題を読んでいなかったので自然数という前提を無視して考えていたのだが、自然数に限ってしまえば上記の公式は無用になる。


たとえば問題の例にある2つのinput


1 2 3 4
1 2 4 8
3項目、4項目だけ見てやれば、ある特徴に気が付く。等差数列の場合、4項目÷3項目が割り切れないのに対して等比数列の場合は必ず割り切れる。

ロベールさんの解説によれば、


等差数列とすると b a+b 2a+b 3a+b の形になる。
もし 3a+b が 2a+b の倍数になりえなければ、
最後の項がその前の項で割り切れれば等比数列
割り切れなければ等差数列になる。


1. a < 0 の場合
2a+b > 3a+b なので、
3a+b は 2a+b の倍数になりえない。


2. a > 0 の場合
3a+b = (2a+b) + a が 2a+b の倍数になるには、
a が 2a+b の倍数でないといけない。
しかし、a, b 共に自然数なので a < 2a+b となり、
これはありえない。


3. a = 0 の場合
これは等差数列かつ等比数列なので、
どちらと判定されても構わない。

ということになり、自然数に限れば3項目・4項目を見るだけで等差・等比の判別が出来るというわけだ。


よって、コンパイラ非依存の最短コードは


main(n,c,d){for(;~scanf("%d",&d);c=d)--n&&printf(n%4?"%d ":"%d %d\n",d,d%c?d+d-c:d*d/c);}
となり、89byte。


コンパイラ依存で書けば、


main(n,c,d){for(;~scanf("%d",&d);)--n&&printf(n%4?"%d ":"%d %d\n",c=d,d%c?d+d-c:d*d/c);}
で88byte。


おそらくこれ以上短いコードは書けない。


ちなみに、私が昨日のコメントで書いた90byteのコードと言うのはこれ。


main(n,c,d){for(;~scanf("%d",&d);)--n&&printf(n%4?"%d ":"%d %d\n",c=d,d/c>1?d*d/c:d+d-c);}
2・3項位では保証されないが、4項目まで来るとこれで通る。テストケースが甘くて助かったという感じだ。