5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

paiza x Qiitaコラボ企画に乗っかってみました。

問題概要

問題文はこちら
トランプの神経衰弱です。
最初にカードの配置と各ターンでどの位置のカードが捲られたかの情報が与えられ、それを元に各メンバーが何枚カードをゲットしたか出力します。
与えられる入力のフォーマットは下の通りです。

H W N                        # トランプは縦H枚、横W枚並べられ、プレイヤーはN人
t_{1,1} t_{1,2} ... t_{1,W}  # H行にわたってトランプの番号の情報が与えられる
t_{2,1} t_{2,2} ... t_{2,W}
...
t_{H,1} t_{H,2} ... t_{H,W}
L                # 捲られたトランプの情報が以降L行に渡って与えられる
a_1 b_1 A_1 B_1  # 1ターン目ではa_1行b_1列のトランプとA_1行B_1列のトランプが捲られた
a_2 b_2 A_2 B_2
...
a_L b_L A_L B_L

例えば下の入力例では、

  • 縦2枚、横3枚のトランプが並べられ、プレイヤーは2人(1行目)
  • 置かれたカードの情報(2~3行目)
  • 捲られたカードの情報数(4行目)
  • 各ターンで捲られたカード情報(5行目~)
    例えば1ターン目では(1,1)と(2,1)の場所のカードが捲られた

ということがわかります。

2 3 2
1 2 3
2 1 3
5
1 1 2 1
1 1 1 2
1 1 2 2
1 3 2 3
1 2 2 1

自分の解答

# 入力値の受け取り
H,W,N = gets.split.map(&:to_i)
cards = H.times.map { gets.split.map(&:to_i) }
L = gets.to_i
turns = L.times.map { gets.split.map(&:to_i) }

# 各プレイヤーが何枚カードをゲットしたか記録するハッシュ
result = {}
N.times do |i|
  result[i] = 0
end

# プレイヤーの交代回数を記録する変数
count = 0

# 各ターン毎に、捲ったカードの数字が一致したかどうかで場合分けをする
turns.each do |a1,b1,a2,b2|
  if cards[a1-1][b1-1] == cards[a2-1][b2-1]
    result[count % N] += 2
  else
    count += 1
  end
end

# 各プレイヤーの保持するカード枚数を出力
puts result.values

解説

入力値の受け取り

H,W,N = gets.split.map(&:to_i)
cards = H.times.map { gets.split.map(&:to_i) }
L = gets.to_i
turns = L.times.map { gets.split.map(&:to_i) }

ここではgetsメソッドで標準入力から値を取得しています。
公式リファレンス/getsメソッド
getsメソッドは与えられた入力を1行、文字列として読み取ります。

gets.split.map(&:to_i)では、getsで取得した文字列をsplitメソッドで分割し、map(&:to_i)で各要素をIntegerに変換しています。

cards = H.times.map { gets.split.map(&:to_i) }では、上記の作業をH回行うことで、cards変数にトランプの置き場所の情報が二次元配列として格納されます。
上の入力例であれば、[[1, 2, 3], [2, 1, 3]]となります。

同様に、turns変数には各ターンでめくったカード情報を二次元配列で格納しています。

ハッシュ、変数の用意

次に、必要な数値を記録するための変数を用意します。

# 各プレイヤーが何枚カードをゲットしたか記録するハッシュ
result = {}
N.times do |i|
  result[i] = 0
end

上記のハッシュは、キーをプレイヤーの番号(0 ~ N-1)、値をそのプレイヤーがゲットしたカードの枚数として記録します。

# プレイヤーの交代回数を記録する変数
count = 0

捲ったカードの数字が一致しなかった場合は次のプレイヤーに交代します。
交代回数を記録するcount変数を用意しました。

各ターン毎の処理

# 各ターン毎に、捲ったカードの数字が一致したかどうかで場合分けをする
turns.each do |a1,b1,a2,b2|
  if cards[a1-1][b1-1] == cards[a2-1][b2-1]
    result[count % N] += 2
  else
    count += 1
  end
end

各ターン毎に行う処理です。

  • 捲ったカードの数字が一致した場合
    該当プレイヤーのカード枚数を2枚増やす
  • 捲ったカードの数字が一致しなかった場合
    プレイヤーの交代回数を1増やす

【ポイント】

  • 二次元配列cardsのindexは0から始まりますが、カードの場所として与えられる入力値は1から始まります。そのずれを調整するため、cards[a1-1][b1-1]と記述しています
  • 現在のプレイヤー番号の割り出し方法
    プレイヤーの交代回数をプレイヤーの人数Nで割った余りcount % Nが、現在のプレイヤーの番号となります。
    なお、解答例ではプレイヤーの番号を変数で保持しておき、プレイヤー交代時は
    player += 1
    player = 1 if player == N + 1
    
    としていました。
    要はプレイヤーの順番が最初の人に戻るのをどのように表現するかですが、解答例の方が直感的にわかりやすいのでこちらの方が良かったなと思います。

出力

puts result.values

Hash#valuesでハッシュの値のみを得ることができます。

終わりに

記事にすることで、「こういう書き方をする意図は?」「もっといい書き方があるのでは?」と考えるきっかけになりました。

参考文献

プログラミング言語 Ruby リファレンスマニュアル

5
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?