日別アーカイブ: 2022年11月15日

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