Ruby」カテゴリーアーカイブ

PaizaAランクLvアップ問題と解説(Ruby)その10(累積和の計算)

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

1:累積和の計算 (paizaランク C 相当)

数列 A についての情報が与えられるので、 1 ~ N の各 i について、和 S_i = A_1 + … + A_i を求めてください。

入力される値

N       
A_1 A_2 ... A_N
・ 1 行目には、数列 A の要素数 N が与えられます。
・ 2 行目には、数列 A の各要素 A_1, A_2 … A_N が与えられます。入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。
期待する出力
N 行の出力

・S_i(1 ≦ i ≦ N) を出力してください。

S_1     
...     
S_N
条件

すべてのテストケースにおいて、以下の条件をみたします。
・ 1 ≦ N ≦ 10 ^ 5
・ -100 ≦ A_i ≦ 100
入力例1

10
0 1 2 3 4 5 6 7 8 9
出力例1

0
1
3
6
10
15
21
28
36
45
入力例2

1
100
出力例2

100

解答と解説

1:総当り法(良くない方法パフォーマンスが良くない)

計算量がO(N²)となってしまい。めちゃくちゃ時間がかかってしまいます。

#1行目の入力値(数値)を変数に代入する
n = gets.to_i

#2行目の半角スペース刻みの入力値(数値)を変数に多重代入する
m = gets.split.map(&:to_i)

#n回繰り返す
n.times do |i|
 
#mの配列の0(初めから)からmまでの範囲を足したものを出力する
  puts m[0..i].sum 

end

計算量がO(N²)となってしまい。↑のようにテスト4でめちゃくちゃ時間がかかってしまいます。

2:別解    いい方法(パフォーマンスが良い)

1:変数xを予め用意して(変数名はわかりやすいものなら何でもOKです)0で初期化します
2:変数xに現在の値m[i]を加算して出力します。

#1行目の入力値(数値)を変数に代入する
n = gets.to_i
#2行目の半角スペース刻みの入力値(数値)を変数に多重代入する
m = gets.split.map(&:to_i)
x = 0

#n回繰り返す
n.times do |i|
#xに加算して現在の値m[i]を加算して出力する。
 puts x += m[i]
end

計算量がO(N)になったことでテスト4が劇的に速くなりました!

↑のコードを短くすると

n = gets.to_i
m = gets.split.map(&:to_i)
x = 0
n.times { |i| puts x += m[i] }

なぜ解答1は実行時間が遅いのか?(入力例1で解説)

解答1の場合このような毎回0から加算していく計算をするので計算数が増えてしまいます。

0                                                                                計算回数0回  0を出力

0+1=1                                計算回数1回  1を出力

0+1+2=3                           計算回数2回  3を出力

0+1+2+3=6                計算回数3回  6を出力

0+1+2+3+4=10             計算回数4回  10を出力

0+1+2+3+4+5=15           計算回数5回  15を出力

0+1+2+3+4+5+6=21         計算回数6回  21を出力

0+1+2+3+4+5+6+7=28       計算回数7回  28を出力

0+1+2+3+4+5+6+7+8=36     計算回数8回  36を出力

0+1+2+3+4+5+6+7+8+9=45   計算回数9回  45を出力

nが10付近になると膨大な計算数になってしまいます

なぜ解答2で実行時間が速くなるのか?

結論:変数xに加算していき、加算したあとの変数xを出力することで計算数を減らせたので速くなりました。

私達人間が一般的に頭の中で計算して累積和を出す計算方法を使う。

0       計算回数0回
0+1=1   計算回数1回
1+2=3   計算回数1回
3+3=6   計算回数1回
6+4=10  計算回数1回
10+5=15 計算回数1回
15+6=21 計算回数1回
21+7=28 計算回数1回
28+8=36 計算回数1回
36+9=45 計算回数1回

というように

これまでに足した数(例:45)と今計算したい数字(例:10)を加算して答えを出します。

解答2はこれまでに足した数x今計算したい数字m[i]を足して、加算したあとに出力しているので計算数を減らすことができたので実行時間を早くすることができました!

再帰関数で階乗を求める (Ruby)

目標

アルゴリズムを利用した方法で再帰関数がよく使われるので練習して使えるようにする

そもそも再帰関数とは?

アルゴリズムなどを記述するときに、自分自身を引用する形で定義することを再帰的定義といい
自分自身を呼び出す関数のことを再帰関数といいます。

再帰関数の基本的なルールは2つです。

1:自分自身の関数を用いて、問題を少し簡単にするための計算式をたてる
2:簡単なパターンの場合の答えを用意する

階乗を求める関数factorialを用いて問題を解いてみましょう。

階乗(factorial)を求めるメソッド

def factorial(n) 
  if n <= 0  
 #0! = 1なので1を返す
     return 1
  else
   # 動作確認用
   p n
   #ここでfactorial自身を呼び出す
   return n * factorial(n-1) 
  end
end
p factorial(5) #120

5! = 120なので問題ないですね。

p nで動きを確認すると・・・

5
4
3
2
1

↑のようにnの値が変化していることを確認できます。

p n * fact(n-1) で動きを確認すると・・・

1
2
6
24
120

↑のように出力されることが確認されます。
これは再帰関数を使わない、each,times,whileなどのようなループとは挙動が違い、
初めて使う場合はなぜこのようになるのか混乱してしまいます。

そこでpコマンドなどで処理の流れを見ることでどうなっているのか確認します。

p “#{n} * #{fact(n-1)}” で処理を確認してみると・・・

"1 * 1"
"2 * 1 * 1"
"3 * 2 * 1 * 1"
"4 * 3 * 2 * 1 * 1"
"5 * 4 * 3 * 2 * 1 * 1"
"5 * 4 * 3 * 2 * 1 * 1"

↑のように計算していることがわかりますね。

実際はこのように動いているようです。

1:factorial(5)はfactorial(4) × 5 を返すことになっているので、関数factorial(4)を呼び出します。
2:factorial(4)はfactorial(3) × 4 を返すことになっているので、関数factorial(3)を呼び出します。
3:factorial(3)はfactorial(2) × 3 を返すことになっているので、関数factorial(2)を呼び出します。
4:factorial(2)はfactorial(1) × 2 を返すことになっているので、関数factorial(1)を呼び出します。
5: factorial(1)はfactorial(0) × 1 を返すことになっているので、関数factorial(0)を呼び出します。
6:factorial(0)はN<=0の条件をみたすので1を返します。
7:factorial(1)はfactorial(0)の呼び出しが終わったので1 × 1 = 1を返します。
6:factorial(2)はfactorial(1)の呼び出しが終わったので2 × 1 × 1 = 2を返します。
7:factorial(3)はfactorial(1)の呼び出しが終わったので3 × 2 × 1 × 1= 6を返します。
8:factorial(4)はfactorial(1)の呼び出しが終わったので4 × 3 × 2 × 1 × 1 = 24を返します。
9:factorial(5)はfactorial(1)の呼び出しが終わったので5 × 4 × 3 × 2 × 1 × 1 = 120を返します

これにより5~9の部分が出力されたので1,2,6,24,120というように出力されました。

PaizaAランクLvアップ問題と解説(Ruby)その6

3から続きます

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

マップの判定・縦横

1:移動が可能かの判定・方角 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ で、移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H が与えられます。
続けて現在の座標 sy , sx ,1マス移動する方角 m が与えられるので、移動が可能かどうかを判定してください。

移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx m     
S_0    
...     
S_(H-1)
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 1 マス移動する方角 m が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)入力値最終行の末尾に改行が1つ入ります。
期待する出力
移動が可能である場合 “Yes” を、不可能である場合 “No” を出力してください。

Yes

または

No

条件

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

・ 1 ≦ H, W ≦ 20
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ m は、N, S, E, W のいずれかであり、それぞれ 北・南・東・西 を意味します。
入力例1

3 3 1 1 E
..#
..#
...
出力例1

No
入力例2

9 2 4 0 S
#.
#.
..
##
..
..
.#
..
.#
出力例2

Yes

解答と解説(入力例1で行います)

#方角のみ文字列なので、それ以外を数値として変数に代入して、mに文字列として代入する
h,w,y,x,m = gets.split.map.with_index { |val, i| i == 4 ? val : val.to_i }

#盤面の情報をバラバラにして代入する 結果→[[".", ".", "#"], [".", ".", "#"], [".", ".", "."]]
chart = h.times.map { gets.chomp.chars}

#移動操作した時の座標の移動パターンを作成する。
direction = { N: [-1, 0], S: [1, 0], E: [0, 1], W: [0, -1] } 
#入力例1の場合mがEなので[0, 1]yとxに加算する
y += direction[m.to_sym][0]
x += direction[m.to_sym][1]

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."であることを満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts "Yes"
  それ以外を"No"と出力する
  else puts "No"
end

2:移動が可能かの判定・方向 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ , 移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H が与えられます。
続けて現在の座標 sy , sx , 現在向いている方角 d , 1マス移動する方向 m が与えられるので、移動が可能かどうかを判定してください。

移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx d m      
S_0     
...     
S_(H-1)
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 現在向いている方角 d , 1 マス移動する方向 m が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
入力値最終行の末尾に改行が1つ入ります。
期待する出力
移動が可能である場合 “Yes” を、不可能である場合 “No” を出力してください。

Yes

または

No
条件

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

・ 1 ≦ H, W ≦ 20
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ d は、N, S, E, W のいずれかであり、それぞれ 北・南・東・西 を意味します。
・ m は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

2 6 0 4 E L
####..
##..#.
出力例1

No
入力例2

7 9 6 0 S R
..#.#..##
..#..#...
#.......#
#.#...###
#.##....#
.....#...
..##..#.#
出力例2

No

解答と解説(入力例1で行います)

#dとmが文字列なので、それ以外を数値として変数に代入して、dとmに文字列として代入する 
h,w,y,x,d,m = gets.split.map.with_index { |val, i| i >= 4 ? val : val.to_i }

#盤面の情報をバラバラにして代入する  結果→[["#", "#", "#", "#", ".", "."], ["#", "#", ".", ".", "#", "."]]
chart = h.times.map { gets.chomp.chars}

#Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 
#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 

#入力例1の場合mがLで向いている方向がEなので[-1, 0]yとxに加算する
if m == "R"
  y += operateR[d.to_sym][0] 
  x += operateR[d.to_sym][1] 
elsif m == "L"
  y += operateL[d.to_sym][0] 
  x += operateL[d.to_sym][1] 
end

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."である条件を満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts "Yes"
  else puts "No"
end

※移動後の座標が[-1,4]で画面外なので”No”が出力された

3:移動が可能かの判定・複数回の移動 (paizaランク B 相当)

マップの行数 H と列数 W , 現在の座標 sy , sx , 移動の回数 N が与えられます。
続けて、障害物を ‘#’ で、移動可能な場所を ‘.’ で表した H 行 W 列 のマップ S_1 … S_H と N 回の移動の向き d_1 … d_N が与えられます。

移動者ははじめ北を向いています。移動者は、1 回の移動で次の行動を行います。

「移動の向きに方向転換したのち、1 マス進む。」

各移動が可能である場合、移動後の y , x 座標を出力してください。
移動が可能でない場合、移動後の座標を出力する代わりに “Stop” を出力して、以降の移動を打ち切ってください。

各移動が可能であるということは、以下の図の通り
「移動先が障害物でない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


右に移動

入力される値

H W sy sx N     
S_0     
...     
S_(H-1)     
d_1     
...     
d_N
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 移動する回数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
・ 続く N 行のうち i 行目 (1 ≦ i ≦ N) には、i 回目の移動の向き d_i が与えられます。
入力値最終行の末尾に改行が1つ入ります。
期待する出力
M (1 ≦ M ≦ N) 行の出力

・ k (1 ≦ k ≦ M) 回目の移動後の y , x 座標、y_k, x_k を出力してください。
・ ただし、M 回目で移動しきれない場合、”Stop” を出力してください。

y_1 x_1
...
y_k x_k
...
y_M x_M

または

y_1 x_1
...
y_k x_k
...
y_(M-1) x_(M-1)
Stop
条件

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

・ 1 ≦ H, W ≦ 20
・ 1 ≦ N ≦ 100
・ 0 ≦ sy < H , 0 ≦ sx < W
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S_i の各文字は '.' または '#'
・ d_i は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

7 3 2 1 5
..#
...
...
...
..#
.#.
##.
L
L
L
L
L
出力例1

2 0
3 0
3 1
2 1
2 0
入力例2

7 11 1 5 43
.##........
.#......##.
.#....#...#
.###......#
#......###.
..#....###.
#.#........
L
L
R
L
R
L
R
L
L
R
L
R
L
L
L
L
R
R
R
L
R
L
R
L
L
R
L
L
R
L
R
L
R
R
R
R
L
R
L
L
L
R
R
出力例2

1 4
2 4
2 3
Stop

解答と解説(入力例2で行います)

直感的でわかりやすいけどあまりよろしくないかも

#数値として各変数に代入する
h,w,y,x,n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する 実行 → [[".", "#", "#", ".", ".", ".", ".", ".", ".", ".", "."], [".", "#", ".", ".", ".", ".", ".", ".", "#", "#", "."], [".", "#", ".", ".", ".", ".", "#", ".", ".", ".", "#"], [".", "#", "#", "#", ".", ".", ".", ".", ".", ".", "#"], ["#", ".", ".", ".", ".", ".", ".", "#", "#", "#", "."], [".", ".", "#", ".", ".", ".", ".", "#", "#", "#", "."], ["#", ".", "#", ".", ".", ".", ".", ".", ".", ".", "."]]
chart = h.times.map { gets.chomp.chars}

# Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 

#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 

#最初は北を向いているのでdに”N”を代入する
d = "N"

#コマンドがn回あるのでn回繰り返す
n.times do
  #RかLの入力値を受け取り代入する
  command = gets.chomp

#自分が向いている向きdとコマンドL,Rによってyとxを加算する
  if command == "R"
     y += operateR[d.to_sym][0] 
     x += operateR[d.to_sym][1] 
  elsif command == "L"
     y += operateL[d.to_sym][0] 
     x += operateL[d.to_sym][1] 
  end

#自分の向いている向きとコマンドのLかRかで自分の向いている向きを変更する。
  if d == "N" && command == "L" || d == "S" && command == "R"
     d = "W"
  elsif d == "W" && command == "L" || d == "E" && command == "R"
     d = "S"
  elsif d == "E" && command == "L" || d == "W" && command == "R"
     d = "N"
  elsif d == "S" && command == "L" || d == "N" && command == "R"
     d = "E"
  end

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."である条件を満たしたときに"Yes"
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
     puts y.to_s + " " + x.to_s

条件を満たさなかったときに”Stop”を出力して繰り返しを終わらせる(break)
  else puts "Stop"
     break
  end
end

別解

#数値として各変数に代入する
h, w, y, x, n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する 実行 → [[".", "#", "#", ".", ".", ".", ".", ".", ".", ".", "."], [".", "#", ".", ".", ".", ".", ".", ".", "#", "#", "."], [".", "#", ".", ".", ".", ".", "#", ".", ".", ".", "#"], [".", "#", "#", "#", ".", ".", ".", ".", ".", ".", "#"], ["#", ".", ".", ".", ".", ".", ".", "#", "#", "#", "."], [".", ".", "#", ".", ".", ".", ".", "#", "#", "#", "."], ["#", ".", "#", ".", ".", ".", ".", ".", ".", ".", "."]]
chart = h.times.map { gets.chomp.chars }

#移動するパターンを作成する 北 東 南 西 の順番
direction = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#状態を定義する。最初は0
current = 0

#コマンドがn回あるのでn回繰り返す
n.times do
 #RかLの入力値を受け取り代入する
  d = gets.chomp
 #Lの時−1してRのときに+1する
  d == "L" ? current -= 1 : current += 1
  y += direction[current % 4][0]
  x += direction[current % 4][1]

  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts y.to_s + " " + x.to_s
# 条件を満たさなかったときに”Stop”を出力して繰り返しを終わらせる(break)
  else puts "Stop"
    break
  end
end

入力例2

1ループ目

最初の状態はcurrent = 0でコマンドがL、yとx が[1,5]
currentが  -1になり
-1 % 4 は 3なので
direction[3]は[0,-1]
yとxに [0,-1]を加算して
yとx が[1,4]

2ループ目

最初の状態はcurrent = -1でコマンドがL、yとx が[1,4]
currentが  -2になり
-2 % 4 は 2なので
direction[2]は[1,0]
yとxに [1,0]を加算して
yとx が[2,4]

3ループ目

最初の状態はcurrent = -2でコマンドがR、yとx が[2,4]
currentが  -1になり
-1 % 4 は 3なので
direction[3]は[0,-1]
yとxに [0,-1]を加算して
yとx が[2,3]

4ループ目

最初の状態はcurrent = -2でコマンドがL、yとx が[2,3]
currentが  -2になり
-2 % 4 は 2なので
direction[2]は[1,0]
yとxに [1,0]を加算して
yとx が[3,4]
移動先が”#”があるので”Stop”を出力してループを抜ける

※ポイント

d == ‘L’ ? current -= 1 : current += 1
でcurrentの値を変えるだけで移動操作が表現できる。

4:移動が可能かの判定・幅のある移動 (paizaランク B 相当)

マップの行数 H と列数 W , 障害物を ‘#’ で移動可能な場所を ‘.’ で表した H 行 W 列のマップ S_1 … S_H , 現在の座標 sy, sx, 移動の回数 N が与えられます。
続けて、 N 回の移動の向き d_1 … d_N と移動するマス数 l_1 … l_N が与えられます。

プレイヤーははじめ北を向いています。

各移動が可能である場合、移動後の y , x 座標 を出力してください。
移動が可能でない場合(移動しきれない場合)、移動できるところまで移動した後の座標を出力した後に “Stop” を出力して、以降の移動を打ち切ってください。

各移動が可能であるということは、以下の図の通り
「今いるマスから移動先のマスまでに障害物がない かつ 移動先がマップの範囲外でない」
ということを意味します。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。


入力される値

H W sy sx N        
S_0         
...     
S_(H-1)     
d_1 l_1     
...     
d_N l_N
・ 1 行目にはマップの行数を表す整数 H , マップの列数を表す整数 W , 現在の y, x 座標を表す sy sx , 移動する回数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、マップの i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、マップの i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
・ 続く N 行のうち i 行目 (1 ≦ i ≦ N) には、i 回目の移動の向き d_i と移動するマス数 l_i が与えられます。入力値最終行の末尾に改行が1つ入ります。
期待する出力
M (1 ≦ M ≦ N+1) 行の出力

・ k (1 ≦ k ≦ M) 回目の移動後の y , x 座標、y_k, x_k を出力してください。
・ M 回目で移動しきれない場合、移動できるところまで移動した後の y , x 座標、y_M, x_M を出力した後に “Stop” を出力してください。

y_1 x_1
...
y_k x_k
...
y_M x_M

または

y_1 x_1
...
y_k x_k
...
y_M x_M
Stop
条件

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

・ 1 ≦ H, W ≦ 20
・ 1 ≦ N ≦ 100
・ 0 ≦ sy < H, 0 ≦ sx < W
・ 1 ≦ l_i ≦ 20
・ S_i は W 文字の文字列
・ マップ上の(sy, sx)のマスは必ず '.'
・ S の各文字は '.' または '#'
・ d_i は、L, R のいずれかであり、それぞれ 左・右 を意味します。
入力例1

10 10 6 4 3
..#.....#.
..........
##.#......
#.##....#.
.##.#.....
........#.
.#......#.
.#........
...#......
#.#.......
L 2
R 1
L 4
出力例1

6 2
5 2
5 0
Stop
入力例2

15 15 6 4 7
.......#.......
....#.......#.#
.......#.....#.
.......#.#...#.
#......#.......
#.........#....
..............#
..#...#....#..#
............#..
..#...##......#
##..#..#.#.....
#..............
............#..
...#...........
.#.........#.#.
L 4
L 3
R 4
R 5
L 3
L 2
R 1
出力例2

6 0
9 0
9 0
Stop

解答と解説

少し無駄な部分があるがとりあえずできたほう

#数値として各変数に代入する
h, w, y, x, n = gets.split.map(&:to_i)

#盤面の情報をバラバラにして代入する
chart = h.times.map { gets.chomp.chars }

#移動するパターンを作成する 北 東 南 西 の順番
direction = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#状態をそれぞれ定義する。最初は0
current,stop = 0,0

#コマンドがn回あるのでn回繰り返す
n.times do

 #文字列と数値をそれぞれの変数に代入
  d,move = gets.split.map.with_index { |val, i| i == 0 ? val : val.to_i }

 #Lの時−1してRのときに+1する
  d == 'L' ? current -= 1 : current += 1

 #移動する回数繰り返す
  move.times do |i|

#currentの状態によってyとxを加算する
  y += direction[current % 4][0]
  x += direction[current % 4][1]

#画面外だとアウトなのでyとxが0以上でyがh未満xがw未満であることを記述する
#移動後の座標の現在地(chart[y][x])が"."で繰り返しの最後のときに出力
  if y >= 0 && x >= 0 && y < h && x < w && chart[y][x] == "."
    puts y.to_s + " " + x.to_s if move == i + 1

#画面外や現在地が”#”のとき前の状態に戻してStopを1にして繰り返しを終わらせる
  else y -= direction[current % 4][0]
       x -= direction[current % 4][1]
       stop = 1
       break
  end
  end
# stopが1のときに現在地を出力してから改行して”Stop”を出力して繰り返しを終わらせる
  if stop == 1
     puts y.to_s + " " + x.to_s + "\n" +"Stop"
  break
end
end

PaizaAランクLvアップ問題と解説(Ruby)その5

※スキルチェック問題ではありません。
規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました

その5  座標系での向きの変わる移動 (paizaランク B 相当)

開始時点の x , y 座標、移動の回数 N が与えられます。
続くN行で移動の向き d1 … dN が与えられるので、与えられた順に移動をしたときの各移動後の x , y 座標 を答えてください。
移動者ははじめ北を向いています。

なお、マスの座標系は下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

・ 移動をするごとに向く方角が変わること
・ 移動前に向いている方角によって同じ移動の向きでも座標の変化が違うこと
の 2 点に気をつけてください。
例えば、上の図の状態から右に移動を行った場合、下の図のような状態になります。


入力される値
X Y N       
d1      
...     
dN

・ 1 行目には、開始時点の x , y 座標を表す X , Y, 移動の回数 N が与えられます。
・ 続く N 行 (1 ≦ i ≦ N) には、盤面の i 回目の移動の向きを表す文字 d_i が与えられます。入力値最終行の末尾に改行が1つ入ります。

期待する出力
N 行での出力

・ 各移動後の x , y 座標を出力してください。

x_1 y_1
...
x_N y_N
条件
すべてのテストケースにおいて、以下の条件をみたします。
・ -100 ≦ X, Y ≦ 100
・ 1 ≦ N ≦ 100
・ d は、L, R のいずれかでそれぞれ 左・右 に 1 マス進むことを表す。
入力例1

3 5 1
L
出力例1

2 5
入力例2

-18 45 6
L
L
R
R
L
R
出力例2

-19 45
-19 46
-20 46
-20 45
-21 45
-21 44

解答と解説

x, y, n = gets.split.map(&:to_i)

#初期位置が北なので以下のように配置[北, 東, 南, 西]
operate = [[-1, 0], [0, 1], [1, 0], [0, -1]]

#LとRで移動したときの状態を表す
current = 0
n.times do
  d = gets.chomp

#LかRかで状態を変える
  d == 'L' ? current -= 1 : current += 1

#状態によって移動を変更する
  y += operate[current % 4][0]
  x += operate[current % 4][1]
  puts x.to_s + ' ' + y.to_s
end

※ポイント

初期状態が北向きなのでRだったら東で[0, 1]に進み
Lだったら西なので[0, -1]に進む
-1 % 4 は 3
-3 % 4 は 1
1 % 4 は 1
3 % 4 は 3
となるので
d == ‘L’ ? now -= 1 : now += 1
でnowの値を変えるだけで移動操作が表現できる。

その6に続きます

PaizaAランクLvアップ問題と解説(Ruby)その4

その3から続いてます

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

3:座標系での移動・向き (paizaランク B 相当)

開始時点の y , x 座標 と向いている方角 D が与えられます。
続く 1 行で移動の向き d が与えられるので、その向きに移動した後の y , x 座標 を答えてください。
移動前に向いている方角によって同じ移動の向きでも座標の変化が違うことに気をつけてください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。
以下の図を参考にしてみてください。




入力される値
Y X D       
d

・ 1 行目には、開始時点の y , x 座標を表す Y , X, 現在の向いている方角を表す文字 D が与えられます。
・ 2 行目には、移動の向きを表す文字 d が与えられます。

期待する出力
1 行での出力

・ 移動した後の y , x 座標を出力してください。

y x

 条件

すべてのテストケースにおいて、以下の条件をみたします。
・ -100 ≦ X, Y ≦ 100
・ D は、N, S, E, W のいずれかでそれぞれ 北・南・東・西 を意味する。
・ d は、L, R のいずれかでそれぞれ 左・右 に 1 マス進むことを表す。

入力例1

4 2 N
R

出力例1

4 3

入力例2

6 9 E
R

出力例2

7 9

解答と解説

#1行目の入力値を各変数に代入yとxは数値に変換してDは文字列として代入
y, x, D = gets.split.map.with_index { |val, i| i != 2 ? val.to_i : val }

#移動の向きを表す文字を代入
d = gets.chomp

#Rが来たときの移動パターンを設定する
operateR = { N: [0, 1], S: [0, -1], E: [1, 0], W: [-1, 0] } 
#Lが来たときの移動パターンを設定する
operateL ={ N: [0, -1], S: [0, 1], E: [-1, 0], W: [1, 0] } 
  if d == "R"
    y += operateR[D.to_sym][0] 
    x += operateR[D.to_sym][1] 
  elsif d == "L"
    y += operateL[D.to_sym][0] 
    x += operateL[D.to_sym][1] 
  end
#移動後の座標を出力する
puts y.to_s + ' ' + x.to_s

別解 3項演算子でLとRのときに値を変数に代入するだけ

y, x, now = gets.split.map.with_index { |val, i| i != 2 ? val.to_i : val }
d = gets.chomp
d == 'L' ? direction = -1 : direction = 1
if now == 'N'
  x += direction
elsif now == 'S'
  x -= direction
elsif now == 'E'
  y += direction
elsif now == 'W'
  y -= direction
end
puts y.to_s + ' ' + x.to_s

4:座標系での規則的な移動 (paizaランク B 相当)

開始時点の x , y 座標と移動の歩数 N が与えられます。
以下の図のように時計回りに渦を巻くように移動を N 歩行った後の x , y 座標 を答えてください。

なお、マスの座標系は下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
X Y N

・ 1 行で、開始時点の x , y 座標を表す X , Y, 移動の歩数 N が与えられます。
入力値最終行の末尾に改行が1つ入ります。

期待する出力
1行での出力

・ 移動を N 歩行った後の x , y 座標を出力してください。

x y
条件
すべてのテストケースにおいて、以下の条件を満たします。
・ -100 ≦ X, Y ≦ 100
・ 0 ≦ N ≦ 100
入力例1

0 0 3
出力例1

0 1
入力例2

38 47 27
出力例2

41 47

解答

 

 

 

その5に続きます

PaizaAランクLvアップ問題と解説(Ruby)その3

その2から続いています

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

1:マップからの座標取得 (paizaランク C 相当)

マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 …S_H が与えられます。
要素が ‘#’ になっているマスが 1 つあるので、その y , x 座標 を答えてください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値

H W     
S_0     
...     
S_(H-1)

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
入力値最終行の末尾に改行が1つ入ります。

期待する出力
1行の出力

・ 要素が ‘#’ になっているマスの y , x 座標を 1 行で出力してください。

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

・ 1 ≦ H, W ≦ 20
・ S は W 文字の文字列
・ S の各文字は '.' または '#'
・ '#' のマスは必ず1つ
入力例1

1 1
#
出力例1

0 0
入力例2

3 3
.#.
...
...
出力例2

0 1

解答と解説(入力例2の場合)

#1行目の入力値を各変数に代入
h,w = gets.split.map(&:to_i)

#縦(盤面の行数)の回数分繰り返す
h.times do |i|

#入力値を受け取り改行を打ち消しバラバラにする(例;[".", "#", "."])
  line = gets.chomp.chars
 
  #横(盤面の列数)の回数分繰り返す
  w.times do |j|
  #"#"のときに現在地を出力する(後置if)
    puts i.to_s + " " + j.to_s if line[j] == "#"
  end
end

2:座標系での移動・方角 (paizaランク C 相当)

開始時点の y , x 座標 と移動の回数 N が与えられます。
続く N 行で移動の方角 d_1 … d_N が与えられるので、与えられた順に移動をしたときの各移動後の y , x 座標 を答えてください。

ただし、図の通り、上側( y 軸の負の向き)を北とします。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
Y X N       
d_1     
...     
d_N

・ 1 行目には、開始時点の y , x 座標を表す Y , X, 移動の回数 N が与えられます。
・ 続く N 行 (1 ≦ i ≦ N) には、盤面の i 回目の移動の方角を表す文字 d_i が与えられます。入力値最終行の末尾に改行が1つ入ります。

期待する出力
N 行での出力

・ 各移動後の y , x 座標を出力してください。

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

・ 0 ≦ Y, X, N ≦100
・ d_i は、N, S, E, W のいずれかでそれぞれ 北・南・東・西 を意味する。
入力例1

0 0 1
N
出力例1

-1 0
入力例2

5 10 4
N
W
E
S
出力例2

4 10
4 9
4 10
5 10

解答 普通にやる場合

y,x,n = gets.split.map(&:to_i)
command = readlines.map(&:chomp)
command.size.times do |i|
  if command[i] == "N"
     puts (y-1).to_s + " " + x.to_s 
      y += - 1
  elsif command[i] == "W"
     puts y.to_s + " " + (x-1).to_s
     x += - 1
  elsif command[i] == "E"
     puts y.to_s + " " + (x+1).to_s 
     x += + 1
  elsif command[i] == "S"
     puts (y + 1).to_s + " " + x.to_s 
     y += + 1
  end
end

別解 ハッシュを使う場合(こっちのほうがいいかも)

y, x, n = gets.split.map(&:to_i)
direction = { N: [-1, 0], S: [1, 0], E: [0, 1], W: [0, -1] }
n.times do
  command = gets.chomp
  y += direction[command.to_sym][0]
  x += direction[command.to_sym][1]
  puts y.to_s + ' ' + x.to_s
end

その4に続きます

PaizaAランクLvアップ問題と解説その2(Ruby)マップの判定(横・縦)

1から続いています

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

問題3:マップの判定・横 (paizaランク C 相当)

行数 H , 列数 W の盤面があり、盤面の各マスには文字が 1 つだけ書かれています。
盤面が与えられるので、「左右のマスが “#” 」であるようなマスの座標を全て出力してください。

ただし、左端のマスの場合は「右のマスが “#” 」であれば、右端のマスの場合は「左のマスが “#” 」であれば条件を満たすものとします。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
H W     
S_0     
...     
S_(H-1)

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)

期待する出力
N (1 ≦ N ≦ H×W) 行の出力

・ 条件を満たすマスの y , x 座標を出力してください。
・ 左上 (y = 0, x = 0) のマスから順に、x 座標 , y 座標の順で増加するように出力してください。詳しくは入出力例を参考にしてください。

y_1 x_1
...
y_N x_N

 

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

・ 1 ≦ H, W ≦ 20
・ S は W 文字の文字列
・ S の各文字は "." または "#"
・ 条件を満たすマスが少なくとも 1 つ以上存在します

 

入力例1
3 3
#.#
.#.
...
出力例1
0 1
1 0
1 2
入力例2
4 4
####
####
####
####
出力例2
0 0
0 1
0 2
0 3
1 0
1 1
1 2
1 3
2 0
2 1
2 2
2 3
3 0
3 1
3 2
3 3

何が問われているか?(問題の抽象化)

座標系問題のx軸の判定

解答 ※ifと||で3パターンに対応している

#1行目の半角スペース刻みの入力値を変数に多重代入する
h, w = gets.split.map(&:to_i)

#盤面の情報を配列として代入していく
chart = h.times.map { gets.chomp.chars }

chart.each.with_index do |yoko, line|
  yoko.each_index do |index|

# 左端にいるかどうか(x == 0)または左に#があるかどうか確認することで右端(yoko[x - 1])も対応している
  if index == 0 || yoko[index - 1] == '#'

# 右端(x == w - 1)にいるかどうかまたは右に#があるかどうか(yoko[x + 1] == '#')確認している
# 以上により左右に#があるかがわかる
  if index == w - 1 || yoko[index + 1] == '#'

#条件を満たしたy軸とx軸を半角スペース刻みで出力する
    puts line.to_s + ' ' + index.to_s 
  end
  end
  end
end

問題4: マップの判定・縦 (paizaランク C 相当)

マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 … S_H が与えられるので、
上下のマスがどちらも ‘#’ であるようなマスの y , x 座標 を答えてください。

ただし、上端のマスの場合は「下のマスが ‘#’」であれば、下端のマスの場合は「上のマスが ‘#’」であれば条件を満たすものとします。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
H W     
S_0     
...     
S_(H-1)

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)

期待する出力
N (1 ≦ N ≦ H×W) 行の出力

・ 条件を満たすマスの y , x 座標を出力してください。
・ 左上 (y = 0, x = 0) のマスから順に、x 座標 , y 座標の順で増加するように出力してください。詳しくは入出力例を参考にしてください。

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

・ 1 ≦ H, W ≦ 20
・ S は W 文字の文字列
・ S の各文字は '.' または '#'
・ 条件を満たすマスが少なくとも 1 つ以上存在します
入力例1
3 3
###
...
###
出力例1
1 0
1 1
1 2
入力例2
4 4
#.#.
.#.#
.#.#
#.#.
出力例2
0 1
0 3
3 1
3 3

何が問われているか?(問題の抽象化)

座標系問題のy軸の判定

解答と解説

#1行目の半角スペース刻みの入力値を変数に多重代入する
h, w = gets.split.map(&:to_i)

#盤面の情報を配列として代入していく
chart = h.times.map { gets.chomp.chars }
chart.each.with_index do |yoko, line|
  yoko.each_index do |index|

# 上端(line == 0)にいるかまたは上(a[line - 1 ][index] == '#')に#があるかどうか確認することで下端も対応している
  if line == 0 || a[line - 1 ][index] == '#' 

# 下端(line == h - 1)にいるかまたは下(a[line + 1 ][index] == '#')に#があるかどうか確認している 
  if line == h - 1 || a[line + 1 ][index] == '#'

#条件を満たしたy軸とx軸を半角スペース刻みで出力する
     puts line.to_s + ' ' + index.to_s 
  end
  end
  end
end

※配列は0から始まるので下端が(line == h – 1)となっています・

問題5:マップの判定・縦横 (paizaランク B 相当)

マップの行数 H と列数 W とマップを表す H 行 W 列の文字列 S_1 … S_H が与えられるので、
隣接する上下左右のマスが全て ‘#’ であるマスの y , x 座標 を答えてください。

ただし、左端のマスの場合は「右のマスが ‘#’ 」であれば、右端のマスの場合は「左のマスが ‘#’ 」であれば隣接する左右のマスが全て ‘#’ であるものとします。
また、上端のマスの場合は「下のマスが ‘#’ 」であれば、下端のマスの場合は「上のマスが ‘#’ 」であれば隣接する上下のマスが全て “#” であるものとします。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
H W     
S_0     
...     
S_(H-1)

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)

期待する出力
N (1 ≦ N ≦ H×W) 行の出力

・ 条件を満たすマスの y , x 座標を出力してください。
・ 左上 (y = 0, x = 0) のマスから順に、x 座標 , y 座標の順で増加するように出力してください。詳しくは入出力例を参考にしてください。

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

・ 1 ≦ H, W ≦ 20
・ S は W 文字の文字列
・ S の各文字は '.' または '#'
・ 条件を満たすマスが少なくとも 1 つ以上存在します

入力例1

3 3
##.
###
...

出力例1

0 0
0 2

入力例2

10 10
##########
..........
##########
##########
..........
#.#.#.#.#.
.#.#.#.#.#
#.#.#.#.#.
.#.#.#.#.#
..........

出力例2

6 0
6 2
6 4
6 6
6 8
7 1
7 3
7 5
7 7
7 9

何が問われているか?(問題の抽象化)

座標系問題のx軸とy軸の判定

解答と解説

#1行目の半角スペース刻みの入力値を変数に多重代入する
h, w = gets.split.map(&:to_i)

#盤面の情報を配列として代入していく
chart = h.times.map { gets.chomp.chars }


chart.each.with_index do |yoko, line|
  yoko.each_index do |index|

# 左端にいるかどうかまたは左に#があるかどうか確認することで右端も対応している   
if index == 0 || yoko[index - 1] == '#' 

# 右端にいるかどうかまたは右に#があるかどうか確認することで左端も対応している  
  if index == w - 1 || yoko[index + 1] == '#'

# 上端にいるかまたは上に#があるかどうか確認することで下端も対応している
    if line == 0 || a[line - 1 ][index] == '#'

# 下端にいるかまたは下に#があるかどうか確認している
      if line == h - 1 || a[line + 1 ][index] == '#'
        puts line.to_s + ' ' + index.to_s
      end
    end
  end
end
end
end

その3に続きます

PaizaAランクLvアップ問題と解説(Ruby)その1

※スキルチェック問題ではありません。

規約により公式の解答コードそのままはよろしくないので、
オリジナルのコードにしています。

詳しくはコチラ

なるべくわかりやすい解説を付けました。

マップの判定・縦横

問題1:盤面の情報取得 (paizaランク C 相当)

行数 H , 列数 W の盤面があり、各マスには文字が 1 つだけ書かれています。盤面と N 個の y , x 座標 が与えられるので、与えられた座標の文字を順に出力してください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値

H W N   
S_0     
...     
S_(H-1)    
y_1 x_1     
...     
y_N x_N

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W , 与えられる座標の数を表す整数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。
・ 続く N 行 には、文字を答えるための y_i , x_i 座標が与えられます。(1 ≦ i ≦ N)

期待する出力

N 行の出力

・ i 行目 (1 ≦ i ≦ N) には、i 番目の座標 y_i, x_i の盤面の文字 A_i を出力してください。

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

・ 1 ≦ H , W ≦ 20
・ 1 ≦ N ≦ H × W
・ 0 ≦ y_i < H , 0 ≦ x_i < W (0 ≦ i < N)
・ S は W 文字の文字列
・ S の各文字は . または #

入力例1

3 3 2

###

###

...

2 2

1 1

出力例1

.
#

入力例2

2 2 1
#.
.#
0 1

出力例2

.

何が問われているか?(問題の抽象化)

多次元配列の基礎的な操作方法(出力)

解答と解説(入力例1をベースとしています)

#1行目の入力値を受け取りそれぞれの変数に代入
h,w,n = gets.split.map(&:to_i)
ary = []

#盤面の情報を受取り変数に代入する。
h.times.map do
  ary << gets.chomp
end
n.times do |i|
#半角スペース刻みの入力値を受け取り変数に代入
  y,x = gets.split.map(&:to_i)
#入力例1の時ary[2][2]とary[1][1]を取り出したいわけなので
  puts ary[y][x] 
end

問題2:盤面の情報変更 (paizaランク C 相当)

行数 H , 列数 W の盤面があり、各マスには文字が 1 つだけ書かれています。盤面と N 個 の y , x 座標 が与えられるので、盤面の与えられた座標の文字を “#” に書き換えた後の盤面を出力してください。

なお、マスの座標系は左上端のマスの座標を ( y , x ) = ( 0 , 0 ) とし、
下方向が y 座標の正の向き、右方向が x 座標の正の向きとします。

入力される値
H W N   
S_0       
...     
S_(H-1)     
y_1 x_1     
...     
y_N x_N

・ 1 行目には盤面の行数を表す整数 H , 盤面の列数を表す整数 W , 与えられる座標の数を表す整数 N が与えられます。
・ 続く H 行のうち i 行目 (0 ≦ i < H) には、盤面の i 行目の文字をまとめた文字列 S_i が与えられ、 S_i の j 文字目は、盤面の i 行目の j 列目に書かれている文字を表します。(0 ≦ j < W)
・ 続く N 行 には、 文字を書き換えるマスの y , x 座標が与えられます。(1 ≦ i ≦ N)

期待する出力
H 行での出力

・ 書き換えた後の盤面を H 行で出力してください。

T_0       
...     
T_(H-1)
条件
すべてのテストケースにおいて、以下の条件をみたします。

・ 1 ≦ H , W ≦ 20
・ 1 ≦ N ≦ H × W
・ 0 ≦ y < H, 0 ≦ x < W
・ S は W 文字の文字列
・ S の各文字は "." または "#"
入力例1
3 3 1

...

...

...

0 0
出力例1
#..

...

...
入力例2
4 4 2

####

####

....

##..

2 0

2 2
出力例2
####
####
#.#.
##..

何が問われているか?(問題の抽象化)

多次元配列の基礎的な操作方法(置き換え)

解答と解説

途中までは1と同じだけれども、指定した配列のインデックスの場所を#に変える処理を書けば良い

h,w,n = gets.split.map(&:to_i)
ary = h.times.map { gets.chomp }
n.times do |i|
  y,x = gets.split.map(&:to_i)
# ここが違うよ(指定した位置を#に変える)
  ary[y][x] = "#"
end
puts ary

続きます

TIPS(Ruby:表から移動時間の総和を出す)

目標:なにがしたいか?

表から移動時間の総和を出す

問題

行きたいポイントがリストで与えられるので、与えられた順に各ポイントを訪れた際どれくらい時間がかかるのか求めてください。最初のポイントで始めるところから時間を計るものとします。

入力例1の場合、以下のように、各ポイントの移動時間が与えられています。

各ポイント

1

2

3

 

各ポイント\各ポイント

1

2

3

1

0

3

4

2

3

0

5

3

4

5

0

ポイント 1 でポイントから 2 に移動するのに時間 3 かかります。
ポイント 2 でポイントから 3 に移動するのに時間 5 かかります。
ポイント 3 でポイントから 1 に移動するのに時間 4 かかります。
全部で時間 12 かかります。

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

N
c_{1, 1} c_{1, 2} ... c_{1, N}
c_{2, 1} c_{2, 2} ... c_{2, N}
...
c_{N, 1} c_{N, 2} ... c_{N, N}
K
y_1
y_2
...
y_K
  • ・1 行目に全てのポイントの数を表す整数 N が与えられます。
  • ・続く N 行のうちの i 行目 (1 ≦ i ≦ N) には N 個の整数が半角スペース区切りで与えられます。i 行目の j 番目 (1 ≦ j ≦ N) の整数 c_{i, j} は i 番目のポイントから j 番目のポイントへの移動時間を意味します。
  • ・続く 1 行には、行きたいポイントの数を表す整数 K が与えられます。
  • ・続く K 行のうちの i 行目 (1 ≦ i ≦ K) には、i 番目に訪れたいポイントを表す整数 y_i が与えられます。
  • ・入力は合計で 2 × N + K + 2 行となり、入力値最終行の末尾に改行が 1 つ入ります。
期待する出力
・全行動後にかかる時間を整数で出力してください。
・末尾に改行を入れ、余計な文字、空行を含んではいけません。
条件
すべてのテストケースにおいて、以下の条件をみたします。

・1 ≦ N ≦ 100
・各 i, j (1 ≦ i, j ≦ N) について
 ・1 ≦ c_{i, j} ≦ 100 (i ≠ j)
 ・c_{i, j} = 0 (i = j)
 ・c_{i, j} = c_{j, i} (i ≠ j)
・1 ≦ K ≦ 100
・1 ≦ y_i ≦ N (1 ≦ i ≦ K)
入力例1
3
0 3 4
3 0 5
4 5 0
4
1
2
3
1
出力例1
12
入力例2
4
0 2 9 5
2 0 8 2
9 8 0 3
5 2 3 0
5
1
2
1
3
1
出力例2
22

解答と解説 入力例1で解説

each_consを使えばいとも簡単に解くことができる
移動時間の処理を書く。
配列は0から始まるので全体を-1する
(move.map { |a| a – 1 })
あとは移動時間を加算してあげれば良い

#空の配列で初期化する 
chart,move,ary= [],[],[],[]
#1行目の入力値(数値)を変数に代入する
n = gets.to_i

#n回繰り返し入力値を受け取り配列に代入する
n.times { chart << gets.split.map(&:to_i) }

#全体の時間を司る変数を作成して0で初期化する
total_minutes = 0

#入力値(数値)を変数に代入する
k = gets.to_i

#n回繰り返し入力値を受け取り配列に代入する
k.times.map { move << gets.to_i }

#k回繰り返す
k.times do |i|

# 配列は0から始まるので全体を-1するmove.map { |a| a - 1 }
# [[0, 1], [1, 2], [2, 0]]のような配列を作り次に使えるようにする
# ↑.each_cons(2).map { |x| x } if i == 0
  ary = move.map { |a| a - 1 }.each_cons(2).map { |x| x } if i == 0
# 全体の時間に加算していく
  total_minutes += chart[ary[i][0]][ary[i][1]] if i + 1 != k
end
# 出力する
puts total_minutes

※動作について(k.times do |i|)からend

1回目のループ

ポイント1から2に移動する→配列は0からなのでchart[ary[i][0]][ary[i][1]]の場合

ary = [[0, 1], [1, 2], [2, 0]]

ary[0] = [0,1]

[ary[0][0]] = 0

[ary[0][1]] = 1

chart[ary[0][0]][ary[0][1]] = chart[0][1]

chart = [[0, 3, 4], [3, 0, 5], [4, 5, 0]]

chart[0][1] = 3

total_minutes += 3

total_minutes は3になる

2回目のループ

ポイント2から3に移動する→配列は0からなのでchart[ary[i][0]][ary[i][1]]の場合

ary = [[0, 1], [1, 2], [2, 0]]

ary[1] = [1,2]

[ary[1][0]] = 1

[ary[1][1]] = 2

chart[ary[1][0]][ary[1][1]] = chart[1][2]

chart = [[0, 3, 4], [3, 0, 5], [4, 5, 0]]

chart[1][2] = 5

total_minutes += 5

total_minutes は8になる

3回目のループ

ポイント3から1に移動する→配列は0からなのでchart[ary[i][0]][ary[i][1]]の場合

ary = [[0, 1], [1, 2], [2, 0]]

ary[2] = [2,0]

[ary[2][0]] = 2

[ary[2][1]] = 0

chart[ary[2][0]][ary[2][1]] = chart[2][0]

chart = [[0, 3, 4], [3, 0, 5], [4, 5, 0]]

chart[2][0] = 4

total_minutes += 4

total_minutes は12になる

if i + 1 != k  により4回目のループはないので終わりになる

よって全体の移動時間が12となる

TIPS(Ruby:文字列と数値が混ざったものを1行でそれぞれの変数に入力値を受け取りたい)

目標:プログラミング問題でよくある文字列と数値が混ざったものを1行で入力値を受け取りたい

入力例

3
Tanaka 24
Sato 35
Suzuki 12

実践

#最初の3を変数に代入する

n = gets.to_i

#新たな変数に空の配列を多重代入する。
name,age = [],[]

#n回 (3回)  i == 0 のとき、つまり最初の文字列をnameに文字列として代入
#半角スペースを挟んだ数値をageに数値として代入する。
n.times.map { gets.split.map.with_index { |val, i| i == 0 ? name << val : age << val.to_i } }

#nameとageに期待していたように入っているか確認する。
p name, age

https://paiza.io/projects/4T8pWrwlF0UOH0goc0gopw

↑こちらにコピペして「実行」で確認できます。
↓このような出力結果になっていて、nameに文字列がageに数値が入っていることが確認できます。