旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

【AOJ】花札シャッフル問題(Hanafuda Shuffle)

ICPCというプログラミングコンテストではJavaC++かCを使わないといけないので)サークルの方ではJavaでやっているんですが、普段はRubyで書いていきたいと思います。

問題

カードの山を混ぜて切る方法はいろいろとある. 日本のカード遊びである「花札」における花札混ぜ切りは,札を切る方法の一つ である.

n枚のカードの山がある.図1に示すように,山の一番上からp枚 目の札から連続したc枚の札を抜き取り,それをそのまま山の上に置く. この操作(カット操作という)を繰り返し行う.

花札混ぜ切りをシミュレートし,最終的に山の一番上にくる札を答える プログラムを書きなさい.

書いたコード

class Deck

  def initialize(n)
    @cards = 1.upto(n).to_a
  end 

  def cut(r)
    r.times do
      p,c = gets.split.map(&:to_i)
      bundle = @cards[-p-c+1..-p]
      @cards.slice!(-p-c+1..-p)
      @cards.concat(bundle)
    end 
    @cards
  end 

end

loop do
  input = gets.chomp
  break if input == "0 0"
  n,r = input.split.map(&:to_i)
  deck = Deck.new(n)
  puts deck.cut(r).last
end

山札のクラスを作ってインスタンス変数に配列を入れて、それをごにょごにょする感じで書きました。こういうのを定期的にやってないとメソッド名とかいつまでたっても覚えないですね。

気持ち悪い部分見つけたらツッコミください。

追記

修正しないと気持ち悪いので追記していきます。

gets.split.map(&:to_i)

p_c = gets.chomp.split(" ")
p = p_c[0].to_i
p = p_c[1].to_i

と書いてしまった部分。気持ち悪さは感じてたけど時間制限あるから「これでいいや」的なノリでサブミットしてしまいました。

まず、splitの使い方。引数を省略すれば空白文字で区切ってくれる。つまり、.split(" ").splitでオーケー。で、引数無しのsplitメソッドは先頭・末尾の空白文字も取り除いてくれるのでchompを併用する必要がない。

さらに、splitして返ってくる配列を一度保存してからその1つ1つにアクセスしてる。これはきもい。p_cという変数名もきもい。Rubyではa,b = [1,2]みたいな代入ができるのでそれを使った方がいい。

すると、こうなる。

p,c = gets.split.map(&:to_i)

こちらの記事を参考にさせていただきました。

@cards.concat(fetch_cards(p,c))

これ、2行もいらないのでは。

cards_bundle = fetch_cards(p,c)
@cards.concat(cards_bundle)

ということで1行に。

@cards.concat(fetch_cards(p,c))

fetch_cardsメソッドはいらないのでは

def fetch_cards(p,c)
  bundle = @cards[-p-c+1..-p]
  @cards.slice!(-p-c+1..-p)
  bundle
end

def cut(r)
  r.times do
    p,c = gets.split.map(&:to_i)
    @cards.concat(fetch_cards(p,c))
  end
@cards
end

cutメソッドとfetch_cardsメソッドを分けて書いていたのを、id:sekaiyaさんにご指摘頂きました。fetch_cards単体で使う場面は無いので、この処理はcutメソッドに書いてしまった方がすっきりして良いですね。

def cut(r)
  r.times do
    p,c = gets.split.map(&:to_i)
    bundle = @cards[-p-c+1..-p]
    @cards.slice!(-p-c+1..-p)
    @cards.concat(bundle)
  end
  @cards
end