2015年07月12日
なんとなくやってみました。
今回やってみたのはAtCoderBeginnerContest(以下ABC)の過去問のA問題です。
今回も例によってRubyです。基本的にランキングとか最高記録というのはRubyの中のランキングです。
基本的に過去問では全てのコードが公開されるので複数のコードからいいところをコピペすれば記録は抜けるので微妙なところもあります。
なので記録を縮めるために自分が気づかなかったところとか自分が縮めたところをまとめていきます。最短コードを解説するわけではありません。
一応かなりの問題で新記録を出せているのでそれなりに有効だとは思います。
まあAtCoderはゴルフ場ではないのでどの程度プロゴルファーが参加しているかという問題もありますが。
というわけで1問ずつ
ABC001-A
2つの整数が改行を挟んで入力されてその差を取るだけの問題。何も考えず
p gets.to_i-gets.to_i
とすれば終了です。特に短くするためのテクニック等は必要なく普通に計算すれば終わりです。あえて言うなら出力にpを使うくらいでしょうか。普通に書いても似たようなコードになることも多いようです。数を出力したい時は特に問題なくputsと同じ出力をしてくれます。コード長ランキングを見ると同じ文字数の人が何人かいますね(コードも全く同じです)。
(21Byte→21Byte)
ABC002-A
2つの整数がスペース区切りで与えられ、それの大きいほうを出力すればいいみたいです。
文字列でソートすると辞書順になるので2つの数の桁が違うとちゃんとソートできません。なので比べるときは数に変換する必要があります。
というわけで
p (gets.split.map &:to.i).max
とやれば終了です。と思って提出してランキングを見ると1文字負けてしまいました。原因はカッコの付け方で
gets.split.map(&:to_i).max
と書いていれば一つスペースを減らせます。この書き方は気づきませんでした。ゴルファーにとっては常識なんでしょう(僕はゴルフ初心者です)。
まあ1位の記録とはなりませんでしたが結構短くするための要素はある問題になってます。
まず2つの値を数に変換する為に2つに分割します。この時splitを使いますがこのsplitは引数無しで使うとデフォルトでは半角スペースを指定したものになります。なのでsplit(' ')などとやる必要はないです。
あとよく使われるのがmap &:to_iです。コードゴルフに限らず特にAtCoderではこういう形で値を読み込むことが多いです。普通に使えるので便利ですね。今回は変数に入れずに使ってますが
a=gets.split.map &:to_i
とやれば整数を要素に持つ配列aが宣言できます。また、配列の要素が決まっている時、例えば2個の時は
a,b=gets.split.map &:to_i
とすればa,bは整数として使えます。変数は増えますがa[0]とかやる必要が無いので短く書けることが多いです。問題によってはabcとかとかxyzとかそのまま使えるのでこれも便利ですね。足したり引いたりするには便利です(ソートとかする場合は配列のが便利ですね)。
ちなみに最後のmaxは配列の要素の中から一番大きいものを取り出します。[配列].sort[-1]と同じですね。ちなみに小さいものが欲しいならminです。
(28Byte→29Byte)
ABC003-A
n面のサイコロの期待値の問題です。
サイコロの期待値は(n+1)/2です。
問題はこのサイコロの期待値に10000を掛けた値を出力します。
というわけで普通に考えると
p (gets.to_i+1)*5000
です。簡単ですね。とここで終る問題ではないです。この数式は実は短くなります。まず5000というのは5e3と書くことができます。1文字短くなりましたね。
次に(gets.to_i+1)ですがこれも短くすることができます。ゴルフではとにかくカッコは消せ!という感じで扱われます。かけ算より優先順位の低い足し算を先にやりたいのだから消せないように思えますが次の形で書けます
p 5e3.*gets.to_i+1
こうすることで先に足し算を行うことができます。なんでそうなるかはここに書いてあります。
ここまでで終わりかと思いきやまだ縮められます。
結構有名な方法ですがビット演算を使うことでnが整数の時、n+1を-~nと書くことができます。
n+1と-~nでは長さは変わりませんが式の中ではうまく機能することがあります。
例えば-(n+1)は~nとなるのでこの場合は3文字の削減ですね。
というわけで今回の問題は最終的には次のようになります。
p -~gets.to_i*5e3
ここまでは普通に出るかと思ったら自分が初めてのようです。ビット演算で短くする方法を使ってる人はいませんでした。まあ僕はその前に5000を書いたのを出した後他の人のソースをみて5e3に気づきました。それまでの最高記録のコードは一個前のです。
(19Byte→18Byte)
ABC004-A
単純に入力を2倍するだけなので特に注意することも無いですね。1位タイもけっこういます。
(13Byte→13Byte)
ABC005-A
2つの数字を入力してわり算をするだけです。何故か最高記録を出すことができました。
こんな感じです
a,b=gets.split;p b.to_i/a.to_i
ちなみに他の人とどこが違うかというと他の人は
a,b=gets.split.map &:to_i;p b/a
としてます。後者はまとめてto_iできるので短くなりそうですが実は.map &:to_iより.to_i.to_iのが1文字短いです。2つまでなら分けて書いた方がお得なのです。繰り返しはとにかく避けたくなりますがこういう場合もたまにはあるのです。というわけで2つの変数で合計2回しか.to_iが必要ない時はmapしてやるよりも別々に書いた方が短く書けます。
(31Byte→30Byte)
ABC006-A
入力が3の倍数だったらYES、違ったらNOと出力する問題です。
普通に考えると
puts=gets.to_i%3==0?:YES:"NO"
でしょうか。出力するYES、NOは文字列にするよりシンボルにした方が1文字短くできます。三項演算子では片方は使えない(またはスペースが必要)ので片方は文字列です。
これはまだまだ縮まるんですが、普通に考えるとこうですね。僕も最初はこれを提出しました。
さらにここから%3==0と言うのは1文字短くすることができ、%3<1と書けます。取る値は0,1,2しかないので<1なら0ですね。上の式にこれを加えた文字で1位タイでした。ちなみにタイの1位のコードは判定方法が違います。
ここまでかと思ったらもう一つ短縮する方法がありました。この問題では入力される数が一桁(1≦N≦9)という条件があります。これが実はもう1文字縮められる要因になってました。
ここで使うのはto_iではなくordというメソッドです。
ordは文字の文字コード(数)を返してくれるメソッドです。
例えば"0".ordをすると48が返ってきます。ちなみにgetsの値は"5\n"とかになりますが、複数の文字による文字列にordを使うと最初の文字を数値変換したものが返ってきます。なので"0\n".ordも48です。
"0".ordは48ですが1,2,3...と行くとordも49,50,51...となるのでto_iをそのままordにすれば問題なく同じ式として機能します。最終的にはこうなりました。
puts gets.ord%3<1?:YES:"NO"
記録を1文字更新しました。やったね。
(28Byte→27Byte)
ABC007-A
特に何も書くことがないですね。
(13Byte→13Byte)
ABC008-A
この問題もABC004のと同じようにto_iを分けたら最高記録になりました。ちなみにAtCoderでは改行が2バイト扱いなので改行せず;で1行にした方が1バイト短くなります。
s,t=gets.split;p t.to_i-s.to_i+1
最後の+1はビット演算で消せそうですが先頭に-~と二文字つける必要があるのでビット演算を使っても短くできないですね。
(33Byte→32Byte)
ABC009-A
N/2でNが奇数ならもう1足せばいいようです。というわけで(N+1)/2を使います。
先程も使った~nを使えば良さそうです。
p -~gets.to_i/2
これで終了です。例によってビット演算でn+1を作っていた人はいなかったので最高記録です。
(16Byte→15Byte)
ABC010-A
特に問題はないですね。
getsから改行を取り除くにはgets.chompを使うことが多いですが最後の1文字を消すchopでも問題ないです。1文字短いのでこちらを使います。
(19Byte→19Byte)
ABC011-A
複数の同じ最短コードが提出される簡単な問題です。省略。
(16Byte→16Byte)
ABC012-A
2つの要素を入れ替える問題のようです。splitして配列にした後reverseで逆向きにしてjoin(" ")すればいいようです。ちなみに配列のjoin(" ")は*" "と書くことができます。これで最短コード。結構簡単な問題なようです。
しかし僕はたまたまですが違う方法でやりました。ソースは次のようになります。
a,b=gets.split;puts b+" "+a
この方法を使った人はいませんでしたが次のコードと文字数は一緒です。
puts gets.split.reverse*" "
(27Byte→27Byte)
ABC013-A
gets.ordを使えば簡単に解ける問題でした。"A".ordは65なので
p gets.ord-64
でいいですね。ちなみに最初はordを知らず
p " ABCDE".index(gets.chop)
と書いていました。27文字で2倍以上です。今気づきましたがgets.chopは今回は1文字だけ取得したいのでgets[0]で良かったみたいです。こっちのが少し短いですね。
(20Byte→13Byte)
ABC014-A
2つの数字を入力してなんか計算をするようです。
余りとかを使いそうな雰囲気ですね。
a%bは使いそうですがなんだかちょっと違うようです。
答えは(b-a%b)%bのようです。b-a%bかと思ったら違いました(ちょうど配りきれる時にアウトになるようです)。変数はできるだけ使いたくないですがbは3回出てくるのでbだけ変数を使います。
p (-gets.to_i%(b=gets.to_i)+b)%b
aを先にgets.to_iするために余計なマイナス符号が付いてしまったりカッコが多いですが仕方ないような気がします。ちなみに(変数の文字は違いますが)ここまでの答えはありました(と思ったら無駄なカッコが1組ありました、なぜ?)。
実はもう少し短くする方法があります。もっと簡単な数式で答えが求められます。
でその数式は(-a)%bです。
マイナスの値の割った余りとはなんぞやと思ってしまいますが僕もよくわかってないです。まあこういう式が使える(場合がある)ということは頭の片隅に置いておきましょう。僕は適当に遊んでいたら発見しました。というわけでちょっと長かった数式が一気に短くなりました。使用も1回だけなので変数も使わなくてすみます。というわけでこうなりました。
p (-gets.to_i)%gets.to_i
一気に短くなりました。大勝利です。と思ったらこのカッコもいらないようです。
p -gets.to_i%gets.to_i
ここまでですね。
(34Byte→22Byte)
飽きてきた長くなったので今日はこのくらい。残りの問題はまだ適当です。
今回やってみたのはAtCoderBeginnerContest(以下ABC)の過去問のA問題です。
今回も例によってRubyです。基本的にランキングとか最高記録というのはRubyの中のランキングです。
基本的に過去問では全てのコードが公開されるので複数のコードからいいところをコピペすれば記録は抜けるので微妙なところもあります。
なので記録を縮めるために自分が気づかなかったところとか自分が縮めたところをまとめていきます。最短コードを解説するわけではありません。
一応かなりの問題で新記録を出せているのでそれなりに有効だとは思います。
まあAtCoderはゴルフ場ではないのでどの程度プロゴルファーが参加しているかという問題もありますが。
というわけで1問ずつ
ABC001-A
2つの整数が改行を挟んで入力されてその差を取るだけの問題。何も考えず
p gets.to_i-gets.to_i
とすれば終了です。特に短くするためのテクニック等は必要なく普通に計算すれば終わりです。あえて言うなら出力にpを使うくらいでしょうか。普通に書いても似たようなコードになることも多いようです。数を出力したい時は特に問題なくputsと同じ出力をしてくれます。コード長ランキングを見ると同じ文字数の人が何人かいますね(コードも全く同じです)。
(21Byte→21Byte)
ABC002-A
2つの整数がスペース区切りで与えられ、それの大きいほうを出力すればいいみたいです。
文字列でソートすると辞書順になるので2つの数の桁が違うとちゃんとソートできません。なので比べるときは数に変換する必要があります。
というわけで
p (gets.split.map &:to.i).max
とやれば終了です。と思って提出してランキングを見ると1文字負けてしまいました。原因はカッコの付け方で
gets.split.map(&:to_i).max
と書いていれば一つスペースを減らせます。この書き方は気づきませんでした。ゴルファーにとっては常識なんでしょう(僕はゴルフ初心者です)。
まあ1位の記録とはなりませんでしたが結構短くするための要素はある問題になってます。
まず2つの値を数に変換する為に2つに分割します。この時splitを使いますがこのsplitは引数無しで使うとデフォルトでは半角スペースを指定したものになります。なのでsplit(' ')などとやる必要はないです。
あとよく使われるのがmap &:to_iです。コードゴルフに限らず特にAtCoderではこういう形で値を読み込むことが多いです。普通に使えるので便利ですね。今回は変数に入れずに使ってますが
a=gets.split.map &:to_i
とやれば整数を要素に持つ配列aが宣言できます。また、配列の要素が決まっている時、例えば2個の時は
a,b=gets.split.map &:to_i
とすればa,bは整数として使えます。変数は増えますがa[0]とかやる必要が無いので短く書けることが多いです。問題によってはabcとかとかxyzとかそのまま使えるのでこれも便利ですね。足したり引いたりするには便利です(ソートとかする場合は配列のが便利ですね)。
ちなみに最後のmaxは配列の要素の中から一番大きいものを取り出します。[配列].sort[-1]と同じですね。ちなみに小さいものが欲しいならminです。
(28Byte→29Byte)
ABC003-A
n面のサイコロの期待値の問題です。
サイコロの期待値は(n+1)/2です。
問題はこのサイコロの期待値に10000を掛けた値を出力します。
というわけで普通に考えると
p (gets.to_i+1)*5000
です。簡単ですね。とここで終る問題ではないです。この数式は実は短くなります。まず5000というのは5e3と書くことができます。1文字短くなりましたね。
次に(gets.to_i+1)ですがこれも短くすることができます。ゴルフではとにかくカッコは消せ!という感じで扱われます。かけ算より優先順位の低い足し算を先にやりたいのだから消せないように思えますが次の形で書けます
p 5e3.*gets.to_i+1
こうすることで先に足し算を行うことができます。なんでそうなるかはここに書いてあります。
ここまでで終わりかと思いきやまだ縮められます。
結構有名な方法ですがビット演算を使うことでnが整数の時、n+1を-~nと書くことができます。
n+1と-~nでは長さは変わりませんが式の中ではうまく機能することがあります。
例えば-(n+1)は~nとなるのでこの場合は3文字の削減ですね。
というわけで今回の問題は最終的には次のようになります。
p -~gets.to_i*5e3
ここまでは普通に出るかと思ったら自分が初めてのようです。ビット演算で短くする方法を使ってる人はいませんでした。まあ僕はその前に5000を書いたのを出した後他の人のソースをみて5e3に気づきました。それまでの最高記録のコードは一個前のです。
(19Byte→18Byte)
ABC004-A
単純に入力を2倍するだけなので特に注意することも無いですね。1位タイもけっこういます。
(13Byte→13Byte)
ABC005-A
2つの数字を入力してわり算をするだけです。何故か最高記録を出すことができました。
こんな感じです
a,b=gets.split;p b.to_i/a.to_i
ちなみに他の人とどこが違うかというと他の人は
a,b=gets.split.map &:to_i;p b/a
としてます。後者はまとめてto_iできるので短くなりそうですが実は.map &:to_iより.to_i.to_iのが1文字短いです。2つまでなら分けて書いた方がお得なのです。繰り返しはとにかく避けたくなりますがこういう場合もたまにはあるのです。というわけで2つの変数で合計2回しか.to_iが必要ない時はmapしてやるよりも別々に書いた方が短く書けます。
(31Byte→30Byte)
ABC006-A
入力が3の倍数だったらYES、違ったらNOと出力する問題です。
普通に考えると
puts=gets.to_i%3==0?:YES:"NO"
でしょうか。出力するYES、NOは文字列にするよりシンボルにした方が1文字短くできます。三項演算子では片方は使えない(またはスペースが必要)ので片方は文字列です。
これはまだまだ縮まるんですが、普通に考えるとこうですね。僕も最初はこれを提出しました。
さらにここから%3==0と言うのは1文字短くすることができ、%3<1と書けます。取る値は0,1,2しかないので<1なら0ですね。上の式にこれを加えた文字で1位タイでした。ちなみにタイの1位のコードは判定方法が違います。
ここまでかと思ったらもう一つ短縮する方法がありました。この問題では入力される数が一桁(1≦N≦9)という条件があります。これが実はもう1文字縮められる要因になってました。
ここで使うのはto_iではなくordというメソッドです。
ordは文字の文字コード(数)を返してくれるメソッドです。
例えば"0".ordをすると48が返ってきます。ちなみにgetsの値は"5\n"とかになりますが、複数の文字による文字列にordを使うと最初の文字を数値変換したものが返ってきます。なので"0\n".ordも48です。
"0".ordは48ですが1,2,3...と行くとordも49,50,51...となるのでto_iをそのままordにすれば問題なく同じ式として機能します。最終的にはこうなりました。
puts gets.ord%3<1?:YES:"NO"
記録を1文字更新しました。やったね。
(28Byte→27Byte)
ABC007-A
特に何も書くことがないですね。
(13Byte→13Byte)
ABC008-A
この問題もABC004のと同じようにto_iを分けたら最高記録になりました。ちなみにAtCoderでは改行が2バイト扱いなので改行せず;で1行にした方が1バイト短くなります。
s,t=gets.split;p t.to_i-s.to_i+1
最後の+1はビット演算で消せそうですが先頭に-~と二文字つける必要があるのでビット演算を使っても短くできないですね。
(33Byte→32Byte)
ABC009-A
N/2でNが奇数ならもう1足せばいいようです。というわけで(N+1)/2を使います。
先程も使った~nを使えば良さそうです。
p -~gets.to_i/2
これで終了です。例によってビット演算でn+1を作っていた人はいなかったので最高記録です。
(16Byte→15Byte)
ABC010-A
特に問題はないですね。
getsから改行を取り除くにはgets.chompを使うことが多いですが最後の1文字を消すchopでも問題ないです。1文字短いのでこちらを使います。
(19Byte→19Byte)
ABC011-A
複数の同じ最短コードが提出される簡単な問題です。省略。
(16Byte→16Byte)
ABC012-A
2つの要素を入れ替える問題のようです。splitして配列にした後reverseで逆向きにしてjoin(" ")すればいいようです。ちなみに配列のjoin(" ")は*" "と書くことができます。これで最短コード。結構簡単な問題なようです。
しかし僕はたまたまですが違う方法でやりました。ソースは次のようになります。
a,b=gets.split;puts b+" "+a
この方法を使った人はいませんでしたが次のコードと文字数は一緒です。
puts gets.split.reverse*" "
(27Byte→27Byte)
ABC013-A
gets.ordを使えば簡単に解ける問題でした。"A".ordは65なので
p gets.ord-64
でいいですね。ちなみに最初はordを知らず
p " ABCDE".index(gets.chop)
と書いていました。27文字で2倍以上です。今気づきましたがgets.chopは今回は1文字だけ取得したいのでgets[0]で良かったみたいです。こっちのが少し短いですね。
(20Byte→13Byte)
ABC014-A
2つの数字を入力してなんか計算をするようです。
余りとかを使いそうな雰囲気ですね。
a%bは使いそうですがなんだかちょっと違うようです。
答えは(b-a%b)%bのようです。b-a%bかと思ったら違いました(ちょうど配りきれる時にアウトになるようです)。変数はできるだけ使いたくないですがbは3回出てくるのでbだけ変数を使います。
p (-gets.to_i%(b=gets.to_i)+b)%b
aを先にgets.to_iするために余計なマイナス符号が付いてしまったりカッコが多いですが仕方ないような気がします。ちなみに(変数の文字は違いますが)ここまでの答えはありました(と思ったら無駄なカッコが1組ありました、なぜ?)。
実はもう少し短くする方法があります。もっと簡単な数式で答えが求められます。
でその数式は(-a)%bです。
マイナスの値の割った余りとはなんぞやと思ってしまいますが僕もよくわかってないです。まあこういう式が使える(場合がある)ということは頭の片隅に置いておきましょう。僕は適当に遊んでいたら発見しました。というわけでちょっと長かった数式が一気に短くなりました。使用も1回だけなので変数も使わなくてすみます。というわけでこうなりました。
p (-gets.to_i)%gets.to_i
一気に短くなりました。大勝利です。と思ったらこのカッコもいらないようです。
p -gets.to_i%gets.to_i
ここまでですね。
(34Byte→22Byte)
(22:00)