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の位を取得する)

問題

Rubyで配列を降順にするには?

#降順にする

a = [0,9,4,5]

a = a.sort_by { |n| n }.reverse

#実行結果

[9, 5, 4, 0]

問題

Rubyで配列の中で加算しながら1の位を取得する

入力値

62 89

期待する出力

8
7

コードと解説 (入力例1で解説)

#1行目の半角スペース刻みの入力値を文字列として各変数に代入
n, m = gets.split

#n = ["62"] → ["6","2"]→ [6,2]→ [8] → [8] → 8 
n.chars.map(&:to_i).sum.digits.take(1).pop

#m = ["89"] → ["8","9"]→ [8,9]→ [17] → [7] → 7 
m.chars.map(&:to_i).sum.digits.take(1).pop

1:.charsで文字列をバラバラにする[’89’] → [‘8′,’9’]
2:.map(&:to_i)で文字列を数字に変える[‘8′,’9’]→ [8,9]
3:.sumで配列の中身の数字を合計する[8,9]→[17]
4:.digits.take(1)で1の位を取り出す[17]→[7]
5:.popで配列から取り出す[7] → 7

問題

配列内の数字が続いている時グループ化して最小を足し合わせる

8 10 11 12 21 22の場合

[[8], [10, 11, 12], [21, 22]]←このようにして各配列の最小の値を足し合わせる

入力値

6 
8 10 11 12 21 22

期待する出力

39

コードと解説 .chunk_whileを使えば楽にできる

#空の配列で初期化する
answer = []

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

#2行目の半角スペース刻みの入力値を配列に代入してから昇順に並び替える
a = gets.split.map(&:to_i).sort

group = a.chunk_while {|i, j| i + 1 == j }.to_a

#配列の長さで繰り返す
group.size.times do |i|
  #各配列の最小値を配列に挿入する
  answer << group[i].min
end
puts answer.sum

※ポイント

group = a.chunk_while {|i, j| i + 1 == j }.to_a
#実行結果[[8], [10, 11, 12], [21, 22]]

あとは各配列の最小値を.minで取り出して配列に挿入していき
最終的に配列answerを.sumで総和を出すことでできる。

TIPS(#配列の各要素の数値の大きさのランキングを作る) Ruby

目標:何がしたいか?

# 配列の中の数値の大きさのランキングを出力する

コード

arr = [5,6,7,8,1,3,4,5,6,7,8,9,6,34,12]
arr = arr.sort_by{ |i| i }.reverse
arr.each_with_index do |num, index|
  puts "#{num}は#{index + 1}位です"
end

出力すると・・・

34は1位です
12は2位です
9は3位です
8は4位です
8は5位です
7は6位です
7は7位です
6は8位です
6は9位です
6は10位です
5は11位です
5は12位です
4は13位です
3は14位です
1は15位です

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に数値が入っていることが確認できます。

nuxtとRailsでアプリを作る(1)環境構築概要

NuxtとRailsでアプリを作ったときの手順の環境構築概要です。

目標:忘れた時にすぐに思い出せるようにざっくり解説する。

第0章:Railsの環境構築(インストールと確認)

0:Homebrewをインストール

Homebrewから「インストール」の見出しの下にあるスクリプトの行をmacOSのターミナルにコピー&ペーストします。

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew -v

1:rbenvをインストール

brew install rbenv
rbenv --version

2:rbenvにPATHを通す

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'if which rbenv > /dev/null; then eval "$(rbenv init -)"; fi' >> ~/.bash_profile
source ~/.bash_profile

3:Rubyをインストール

rbenv install 3.0.0

4:ローカルで使うRubyのバージョンを指定

rbenv local 3.0.0
ruby -v

5:Bundlerをインストール

gem install bundler
bundler -v

6:yarnをインストール(yarnはJavaScriptのライブラリの利用に必要なパッケージマネージャ)

brew install yarn
yarn -v

7:Railsをインストール

gem install rails -v 6.1.3.1
rails -v

NuxtJSの環境構築(macOS)

パッケージ管理ツールのインストール

Nuxtというフレームワークを使用するためには、
様々なプログラムをインストールする必要があります。

他のフレームワーク(Railsとか)と同じように、パッケージ管理ツールをインストールしていきます。

Railsにはパッケージ管理ツールにGem、
さらにそのGemを管理するBundlerなどが存在しています。
Nuxtの環境構築としてはNpmを利用します。

Npmの説明

Npmの正式名称はNode Package Managerです。

NpmはNode.jsというプログラムに組み込まれています。

Node.jsはJavaScriptを実行する環境です。

本来、JavaScriptはWebブラウザに組み込まれていて、
ブラウザ上でしか実行できませんでした。

しかし、Node.jsはその場でのスクリプトの実行を可能にしてくれます。
さまざまなライブラリが存在しているのですが、それらのインストールを行うツールとして、Node.jsに組み込まれているのが今回使用するNpmなのです。

他のJavaScriptライブラリNpmを使ってインストールができるようになってきています。
NuxtもNpmでインストールできるため、Node.jsをインストールするというわけです。

8:Node.jsとNpmのインストール

brew install node
node -v
npm -v

以上で環境構築は完了です。

TIPS(Ruby:文字列を置き換える)

プログラミング問題などで使えるテクニック

目標:文字列を置き換えたい

例えば母音を消したいときなど

入力例

TANaka

期待する出力

TNk

コード

#入力値を改行コードを除いて文字列として受け取る
s = gets.chomp

#母音(aiueoAIUEO)を空白で置き換えることで消す
puts a.gsub( /[aiueoAIUEO]/ , "" )

https://paiza.io/ja/projects/new

↑こちらにコピペして「実行」で確認できます。

↓このような出力結果になっていて文字列が数値に置き換わっていることがわかります。

変数を使う場合

例:アルファベットを数字に変える

入力例

ABCDE

期待する出力

01234

コード

#入力値を改行コードを除いて文字列として受け取る 
a = gets.chomp 

#変換するパターンを作成する
pattern = a = gets.chomp pattern = { "A" => "0", "B" => "1", "C" => "2", "D" => "3", "E" => "4", "F" => "5"}

#gsubで置き換えて出力
puts a.gsub(/[ABCDE]/, pattern)

https://paiza.io/ja/projects/new

↑こちらにコピペして「実行」で確認できます。

↓このような出力結果になっていて文字列が数値に置き換わっていることがわかります

TIPS(7以上7777777以下の7の倍数を全て書き出したとき、数字7は何回現れるか)Ruby

問題

7以上7777777以下の7の倍数を全て書き出したとき、数字「7」は何回現れるか出力してください

解答と解説

#7以上7777777以下の配列を範囲で作ってから
#7の倍数の数字と文字列に変換してから文字列の7が含まれる配列を作成する
seventh_array = [*7..7777777].select{ |i| i % 7 == 0 && i.to_s.include?('7')}

#↓以下のような配列になっていれば成功している
#[7, 70, 77, 147, 175, 217, 273, 287, 357, 371, 378, 427, 476,・・・7777777 ]

#カウンターを作り0で初期化
counter = 0

#繰り返し処理をする
seventh_array.each do |number|

#文字列の7がいくつあるか.countで数えてカウンターに加算する
  counter += (number.to_s).count("7")

end
# カウンターの数を出力
puts counter

短くすると

#7以上7777777以下の配列を範囲で作ってから、7の倍数の数字のみの配列にする
#文字列に変換してから文字列の7が含まれるものに抽出する
seventh_array = [*7..7777777].select{ |i| i % 7 == 0 && i.to_s.include?('7')}

#カウンターを作り0で初期化
counter = 0

#文字列の7がいくつあるか.countで数えてカウンターに加算する
seventh_array.each { |number| counter += (number.to_s).count("7")  }

# カウンターの数を出力
puts counter

TIPS(Ruby:1から1000の数字に任意の数字が何個含まれるか?)

目標:なにがしたいか?

1から1000の数字に任意の数字が含まれるのは何個あるか?

問題

1から1000の数字に任意の数字がいくつ含まれているか、調べたくなりました。
例えば13の数字が入るのは 13, 113, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 213, 313
413,513,613,713,813,913であるので、合計 20となります。

与えられる入力値

13

期待する出力

20

コードと解説

#1行目の入力値を受け取る
n = gets.chomp

#1~1000まで配列を作成して配列内の数字を全て文字列に変換する
days = [*1..1000].map(&:to_s)

#include?で任意の数字を選択して長さを出力する
puts days.select { |i| i.include?(n) }.size

https://paiza.io/ja/projects/new

↑こちらにコピペして「実行」で確認できます。

↓このような出力結果になっていればOKです。

TIPS(Ruby:特定の数字や1の位10の位の値で条件分岐する)

目標:なにがしたいか?

特定の数字や1の位10の位の値で条件分岐する方法

問題

あなたはマンションに引っ越しを考えています。
しかし、嫌な数字があり、その数字が含まれる部屋番号の入居を避けることにしました。
そこで、今空いている部屋のうち、部屋番号のどの桁にも嫌な数字が含まれていない部屋番号をリストアップして伝えることにしました。

条件

1:一の位に4が含まれる部屋番号 例:104,204,304
2:13が含まれる部屋番号 例:113,213,313
3:部屋番号666

・1行目に部屋の総数 n
・2行目以降に各部屋番号を表す整数 room_i (1 <= i <= m)

が改行区切りで与えられるので、希望するマンションの部屋番号をすべて改行区切りで出力して下さい。

入力される値

入力は以下のように与えられます。

n #部屋の総数
room_1 #1個目の部屋番号
room_2 #2個目の部屋番号
...
room_n #n個目の部屋番号

それぞれの値は文字列で標準入力から渡されます。

与えられる入力値

9
101
130 
304 
401 
501
666
704
813
902

期待する出力

101
130
401
501
902

コードと解説

#1行目の入力値を受け取る
n = gets.to_i

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

#2行名以降の入力値を受け取りバラバラにする 例:["1", "0", "1"]
  numbers = gets.chomp.chars
#1の位が4の場合か666の場合に対応
  if numbers[-1] == "4" || numbers.join == "666"
#1の位が3で10の位が1の場合に対応して13があるかどうか対応する
  elsif numbers[-1] == "3" && numbers[-2] == "1"
# 上記の条件以外のとき部屋番号を出力する
  else puts numbers.join
  end
end

https://paiza.io/ja/projects/new

↑こちらにコピペして「実行」で確認できます。

↓このような出力結果になっていればOKです。

※ポイント

入力値が301のときgets.chomp.charsで[“1”, “0”, “1”]とひとつずつバラバラにすることができる。

今回は3桁の数字だったが4桁以上の場合を考えて、

配列の[-1]を使うことで1の位、[-2]で10の位で条件分岐してやることができる。