paiza プログラミング

[Ruby]paiza Cランクレベルアップメニュー シミュレーション (paizaランク C 相当)の解説

シミュレーション

こんにちは!じゃいごテックのあつしです。

今回はpaiza Cランクレベルアップメニュー からシミュレーションという問題を解説します。

この問題集はwhileループを使った繰り返し処理に関する2個のSTEP問題(C級)FINAL問題(C級)で構成されていて、STEP問題を解いて行けばFINAL問題も解けるはず!となっています。

STEP問題を解いてみる

STEP問題はwhileループの基本問題です。
簡単な解説は付けていますが、難しいと感じたら下記の記事も参考にしてみて下さい。

STEP1: 条件を満たす最小の自然数 (paizaランク C 相当)

STEP1 は10000以上かつ13で割り切れる最小の自然数を求める問題です。

解答例

# [解答例1]
num = 10000
div = 13

while num % div != 0
  num += 1
end

puts num

# [解答例2]
num = 10000
div = 13

puts num + (div - (num % div))

解答例1: 10000から13で割り切れるかを調べて割り切れなければ +1 の処理を繰り返し、割り切れれば繰り返しを抜けて、その数を出力しています。

解答例2: 計算で解答を求めています。

STEP2: シミュレーションの練習 (paizaランク C 相当)

STEP2 は与えられた2つの式で数値を増加させ、整数nを超えるまでに計算した回数を出力する問題です。

解答例

<<EOS

入力例1
6
3 2

出力例1
2

入力例2
10
2 3

出力例2
3

EOS

n = gets.to_i
a, b = gets.split.map(&:to_i)

paiza = kyoko = 1
count = 0
while true
  count += 1
  kyoko += paiza * a
  break if kyoko > n
  paiza += kyoko % b
end

puts count

解答例: while trueで無限ループを設定して、計算回数countを+1しながら指定された2つの式(kyoko = kyoko + paiza * a, paiza = paiza + kyoko % b)を実行します。そして1つ目の式の計算で条件を満たしたときにループを抜けて計算回数countを表示しています。

シミュレーション (paizaランク C 相当) を解いてみる

※ paiza Cランクレベルアップメニューより

問題

カウンター魔法を得意とするパイザ君は、同じくカウンター魔法を使うモンスターと戦っています。バトルはターン制で、パイザ君が先攻で、パイザ君とモンスターで交互に魔法を使い合います。パイザ君の魔法は 1 回目と 2 回目に使うときにはダメージ 1 ですが、 3 回目以降の n 回目には、(モンスターから受けた (n - 1) 回目の攻撃のダメージ) + (モンスターから受けた (n - 2) 回目の攻撃のダメージ) のダメージを与えます。モンスターの魔法はこれよりも強力で、 1 回目と 2 回目には同じくダメージ 1 ですが、 3 回目以降の n 回目には、 (パイザ君から受けた (n - 1) 回目の攻撃のダメージ) * 2 + (パイザ君から受けた (n - 2) 回目の攻撃のダメージ) のダメージを与えます。

パイザ君は自分がどれくらいモンスターの攻撃を耐えられるか知りたいと思っています。パイザ君の体力を H として、両者が同じ魔法を使い続けたとき、モンスターの何回目の攻撃でパイザ君の体力が 0 以下になるかを出力してください。

入力される値

入力は以下のフォーマットで与えられます。

H

1 行目にパイザ君の体力を表す整数 H が与えられます。

入力値最終行の末尾に改行が1つ入ります。

期待する出力

モンスターの何回目の攻撃でパイザ君の体力が 0 以下になるかを出力してください。
末尾に改行を入れ、余計な文字、空行を含んではいけません。

条件

すべてのテストケースにおいて、以下の条件をみたします。

  • 3 ≤ H ≤ 10^8
入力例1
7
出力例1
4
入力例2
35
出力例2
6
攻略ポイント

ポイント

  • whileループ
  • n-1回目、n-2回目の扱いを考える

デバッグを楽にするためにメソッドを作成します。メソッドについては下記の記事も参考にしてみて下さい。

問題を解く流れ

入出力例をコピペしてヒアドキュメントで変数に代入し、データを確認します。正しく受け取れていれば確認用コードは削除します。

INPUT1 = <<~"EOS"
  7
EOS

OUTPUT1 = <<~"EOS"
  4
EOS

INPUT2 = <<~"EOS"
  35
EOS
OUTPUT2 = <<~"EOS"
  6
EOS

# 確認用コード
p INPUT1
# > "7\n"
p INPUT2
# > "35\n"

続いて問題を解くメソッド( solve メソッドとします)を変数の下に定義します。

まず、入力データを受け取る処理を書きます。

def solve(input_line)
  # 標準入力の受け取り
  h = input_line.to_i
end

# 確認用コード
p solve(INPUT1)
# > 7
p solve(INPUT2)
# > 35

データが正しく受け取れていれば、solve メソッドにパイザ君とモンスターのダメージ計算を行う処理を追加していきます。

  • モンスターの攻撃が終わった時点で判定を行うので、無限ループではなくwhileに終了条件を設定します。
  • n-1回目n-2回目の攻撃を参照するために配列を使います。
def solve(input_line)
  # 標準入力の受け取り
  h = input_line.to_i

  # 初期設定
  count = 0
  paiza, monster = [], []

  # h が0を超えている間繰り返す
  while h > 0
    count += 1
    # 2回目までは攻撃は1
    if count < 3
      paiza << 1
      monster << 1
    else
      # [count - 2] -> 前回の攻撃
      # [count - 3] -> 前々回の攻撃
      paiza << monster[count - 2] + monster[count - 3]
      monster << paiza[count - 2] * 2 + paiza[count - 3]
    end
    # [count - 1] -> 今回の攻撃
    h -= monster[count - 1]
  end
  count
end

# 確認用コード
p solve(INPUT1)
# > 4
p solve(INPUT1) == OUTPUT1
# > false
p solve(INPUT2)
# > 6
p solve(INPUT2) == OUTPUT2
# > false

あとは出力形式を整えれば完成です。

p solve(INPUT1)puts solve(STDIN.read) に変更すれば正解となりますが、p solve(INPUT1) == OUTPUT1 -> true を確認するために、結果を改行区切りの文字列に変換して、末尾にも改行を加えます。

def solve(input_line)
  # 標準入力の受け取り
  h = input_line.to_i

  # 初期設定
  count = 0
  paiza, monster = [], []

  # h が0を超えている間繰り返す
  while h > 0
    count += 1
    # 2回目までは攻撃は1
    if count <= 2
      paiza << 1
      monster << 1
    else
      # [count - 2] -> 前回の攻撃
      # [count - 3] -> 前々回の攻撃
      paiza << monster[count - 2] + monster[count - 3]
      monster << paiza[count - 2] * 2 + paiza[count - 3]
    end
    # [count - 1] -> 今回の攻撃
    h -= monster[count - 1]
  end
  # 文字列型に変換して末尾に改行を追加する
  count.to_s << "\n"
end

# 確認用コード
p solve(INPUT1)
# > "4\n"
p solve(INPUT1) == OUTPUT1
# > true
p solve(INPUT2)
# > "6\n"
p solve(INPUT2) == OUTPUT2
# > true

確認用コードを標準入力からのデータ受け取りに変更して、動作確認をしたら提出します。
複数行のデータ受け取りなので STDIN.read を使います。(入力終了は Ctrl+D 又は Ctrl+Z)

解答コード
def solve(input_line)
  # 標準入力の受け取り
  h = input_line.to_i

  # 初期設定
  count = 0
  paiza, monster = [], []

  # h が0を超えている間繰り返す
  while h > 0
    count += 1
    # 2回目までは攻撃は1
    if count < 3
      paiza << 1
      monster << 1
    else
      # [count - 2] -> 前回の攻撃
      # [count - 3] -> 前々回の攻撃
      paiza << monster[count - 2] + monster[count - 3]
      monster << paiza[count - 2] * 2 + paiza[count - 3]
    end
    # [count - 1] -> 今回の攻撃
    h -= monster[count - 1]
  end
  # 文字列型に変換して末尾に改行を追加する
  count.to_s << "\n"
end

puts solve(STDIN.read)
他の解答例
  • 2回目までは 攻撃が 1 で h > 3 がわかっているので、2回目で初期化する。
  • 2回分の攻撃履歴があれば良いので配列の長さを2で固定する。
    ※ 提出解答例だと、条件最大 h = 108 の時、配列の長さは29になる
def solve(input_line)
  h = input_line.to_i

  # 2回目で初期化
  count = 2
  paiza, monster = [1, 1], [1, 1]
  h -= 2

  while h > 0
    count += 1
    # paiza[1], monster[1] -> 前回の攻撃
    # paiza[0], monster[0] -> 前々回の攻撃
    p_atack = monster[1] + monster[0]
    m_atack = paiza[1] * 2 + paiza[0]

    # paiza, monster を上書きする
    paiza = [paiza[1], p_atack]
    monster = [monster[1], m_atack]
    h -= m_atack
  end
  count.to_s << "\n"
end

puts solve(STDIN.read)

今回のまとめ

  • 繰り返し回数が分からない時はwhileループを使うと便利です。
  • 終了条件を間違えて無限ループに入ってしまったらctrl+Cでプログラムを強制終了しましょう。

whileに似たループ処理でuntilやloop doもありますが、とりあえずwhileを覚えておけばなんとかなります!



【PR】アルゴリズム学習でお世話になった本


アルゴリズム関連の技術書は大抵C/C++で書かれていますが、ある程度プログラムが出来れば、処理の流れは理解することが出来ます。






通称: 螺旋本。C++で解説されています。AOJ(Aizu Online Judge)を運営している会津大学の渡辺准教授が書いた本です。データ構造や計算量の内容から丁寧に書いてありますのでアルゴリズム学習の最初の参考書としてオススメです。







通称: 蟻本。C++で解説されています。競技プログラミング中級者の定番書と言われていて、競技プログラミングで利用できる色々なアルゴリズムを学ぶことが出来ます。かなり高度な内容も含まれているので1冊分を完全に理解するのにはかなりの時間がかかりそうですが、手元に置いて何度も読み返しています。







通称: チーター本。C#, C++, JAVAでTopcoderの問題を解説しています。初心者~中級者向けに書かれているので解説が非常に丁寧です。







Python3でアルゴリズムを解説している本です。講義スタイルの本で、図やフローチャートを使ってアルゴリズムとデータ構造についてしっかりと説明されています。代わりにコードは少なめです。Pythonコードが読めなくても十分理解できると思います。


-paiza, プログラミング
-, , , , ,

© 2024 じゃいごテック