※この記事の作者(私)は、既に一つ以上の言語(高級言語)をマスターした後に、新しい言語 JavaScript を習い始めたという記録です。そのため、JavaScript とは違った文であったとしても構造が類似するため、それぞれ後述するコードの解説を行っております。
一応、JavaScript に対しては初心者ですが、他の言語で学んだことと類似しているものは、その点を生かして解説をしております。ご了承ください。
あと真面目には書いていますが、おちゃらけている部分もあります。読む際はご注意ください。
1. 自己紹介
皆さん初めまして、まっちゃラテです。抹茶が大好きな大学2年生です🍵
知っている方は、知っています。(私を知る人はかなりマイナーですが...)
えーと、高1からAtCoderもやっております。現在 茶色 、元緑コーダーでは、あります。実力は保証できない(小声
で!
最近はpaizaに焦点を当て始め(この訳は後述します)、最近、8月初めくらいに 黄色ライン にたどり着きました!!!(結構がんばりました)
あと、これまでに経験したことある言語は
- Java
- C/C++
- C#(あんま書けない...)
- Python3
- PHP(あんま書けない...)
- CASL II
- HTML(あんま書けない...)
です。あんま書けない言語がありますが、基礎的なことはカバーしているつもりではあります。
余談ですが、一番しびれたのは CASL II でした。アセンブラ、いいよね、、、
:
※こうしてみてもらった通り、何にも考えずに文を書いています(おい)。内容はしっかりしていると思われます。いや、しっかりしています。
2. この記事の目的
上で示した通り、現在私はpaizaに熱を入れております。理由は、現実でも適用できるような知識の習得が可能なことはもちろん、paizaさんのサイトにある「スキルチェック」という、与えられた問題を素早く、正確にコードを書き、...っていうのを競うといったシステムが取り入れられております。
このシステムを見てもらうとわかるのですが、問題がすごくたくさんあります!!
で、ここからが言いたい理由の真骨頂(?)なのですが、
とにかく現実志向寄りの問題が多い
ことが、自分の解いた問題の経験上わかったのです!!!
個人的に様々なプログラミングの問題を解いてきましたが、ふとした時paizaさんのサイトで出されている問題を解いてみると、すごく現実的で解きやすい!!問題文が、よくある、なんか、顧客から出されるシステムの満足条件(!?)みたいになっており、もし現実で誰かから「この問題を解くプログラム作って~」って言われた時、すぐ対応できそう!!という、
なんとも現実的思考な問題を好んで解いていたので、私にはすごくぴったりだと思いました。
こうしてpaizaに、はまりました今現在(倒置法)。
:
...で、本題なのですが
実は高校1年生のとき、あるサイトを参考にしながら JavaScript (通称 JS ?) を学んでみたのですが、めっちゃ難しくて挫折しかけたことがあったのです。特に入力方法、というか挫折しすぎて、Javaに浮気しましたが(殴
ということで、ほぼ3年が経った今、このプログラミングの実力で
どこまで簡単にJavaScriptが打てるようになったのかを試したくなり、この記事に、その冒険記として記すことにしました。
なのでそんな厳密な説明は書かれていません!!ご了承ください。
あとJS以外の言語を書いてきているので、これは簡単じゃないだろ!!っていう点が簡単にされている場合があります。ご了承ください。
真面目には、書いています。
それでは、本編です。
:
3. JavaScriptを基礎から学びなおす
最初、paizaのサイトを開いてみると、上部にある「講座一覧」の項目が見えますね。
ここから様々な言語を一から学ぶことができ、すごく便利なツールでした!
今回はJavaScript。よし、やってみます!!
(今回は体験程度なので、「JavaScript 体験編」を見ながら勉強しました)
3.1 標準出力
プログラミングの初歩、標準出力です。このおまじないは以下です。どんっ
console.log("Hello world");
あ
これは
あの頃の青春(高1、JSとの出会い)を思い出す...!!!
解説
console.log(...)
は出力のコードを表し、() の中の "Hello world"
は Hello world
という文字列です。
3.2 コメントアウト
コードを書くときにメモを残すときに使う手法、コメントアウトです※。これは、次のように書くようです。ぽんっ
console.log("Hello world");
// console.log("comment out");
/* console.log("comment out!!"); */
コメントアウト、これはどの言語もあんま変わらんね...
CASL II は ;
、Python は #
...
自分の主言語 C++ は //
なので、これは変わらなくて安心...
解説
1行コメントアウトでは、行中に //
を書くと、//
から行末までがコメントアウトになります。例えば
console.log("Hello world"); // Hello World と出力される
という感じになります。
複数行コメントアウト(範囲コメントアウト)では、/* と */ で囲われたブロック全体がコメントアウトされます。
※2024/8/16 17:32付近追記
コメントアウトとは、厳密にはコードの実行を無効化させる操作を意味します。
またコメントということもありますが、これはコードの説明や注釈としてコードないに残しておくテキストを意味します。
指摘してくれた方、ありがとうございます。
3.3 数値の扱い
どんな言語も、出力に数値計算ができないと不便...と思っていたので、流石にあって安心しました。
このおまじないは次のようになります。こんっ
console.log(12345);
console.log(12345 + 6789);
console.log("12345 + 6789");
まぁまぁ。これは C++ も仕様は同じなので...
解説
- 1 行目: ダブルクォーテーション
"
で囲まれていないですが、これは12345
という数値型として出力されます。 - 2 行目: これも
"
で囲まれず、中間に+
がありますが、これは足し算を表しています。なので、結果として 12345+6789=19134
が出力されます。 - 3 行目: 今度は
"
で囲まれていますね...先述した通り、"
で囲われたものは文字列型としてみなされるので、12345+6789 という文字列ということになります。つまり、12345 + 6789
が出力されます。
ここで経験上の話なのですが、例えば次のコード
console.log(12345 + "6789");
この結果を予想してみてください。
答え
実は次のように出力されます。123456789
これは、数値型と文字列型の結合(=異なる型同士の足し算)をしているので、結果的に文字列として受け取られるためです。
3.4 算術演算子(四則演算)
どの言語も +
、-
、*
、/
、%
はないと困ります!!!あって助かりました。
解説
-
+
は足し算 -
-
は引き算 -
*
は掛け算 -
/
は割り算 -
%
は割り算の余り(剰余)
を表しています。
もちろん四則演算なので ()
にも対応しています。例えば
console.log((10+5)%2);
の出力結果は 1
となります。これは 15÷2 の余り、すなわち 1 であるためです。(15÷2=7あまり1)
3.5 データ型と変数
プログラミングの醍醐味、変数!!このとき学んだおまじないはこうでした。とんっ
let message = "Hello world";
console.log(message);
Oh
見慣れない子が出てきましたね...let...?
この講座で教えている先生曰く、
「 let
か var
、どちらでもいいけど、便宜上 let
を使うといいよ!」
らしいです。3年前は var
を個人的に好んで使ってたんですが、これは、ちょっと気になりますね...でも今回はややこしくならないようにそっとしておきます。
解説
(データ型) (変数名) = (代入したいもの)
とすることで変数を定義できます。
つまり今回データ型は let
、変数名は message
、代入したいものは "Hello world"
(文字列) です。
そして 2 行目の出力で変数 message
に格納されている値を出力しますが、今回は "Hello world"
なので、出力は Hello world
となります。
let
は、数値型も持てるようで、次のコード
let calc = 10 + 2;
console.log(calc * 2);
のように、数値型の変数を定義することもできます。この結果を予想してみてください。
答え
出力は次のようになります。24
これは、変数 calc
に格納された値は 10 + 2
ですが、結果として 12
です。
出力では * 2
とあるので、結果として calc
の値の 2 倍を計算します。したがって出力は 24
となります。
3.6 標準入力(一行)
ついに来ました、プログラミングの決定事項、標準入力...!!!
昔もこの入力のおまじないが覚えられなすぎて挫折した覚えがあります。
ここで学んだおまじないは次のようでした。ろんっ
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let message = lines[0];
console.log(message);
昔はばかみたいに長かったんですよね...
こんなに短く書けるのは、マジで感動しました。技術に感謝です。
解説(厳密ではない+私の予測ですが...)
-
require("fs")
は...fs
っていうのを必要としている、と思いましたが私がわかりませんごめんなさい -
readFileSync("/dev/stdin","utf8")
は、これも予想なのですが()、/dev/stdin は標準入力先を指定している、ファイルのアドレスを指しているような気がします... utf8 は、文字コードを指していると思うのですが、このコードを用いれば日本語を文字化けせずに入力できるので、こうしているのかと思われます... -
split("\n")
は、受け取った入力データを「改行区切り\n
(エスケープシーケンスと言ったりします)」で分割させます。つまり、これはたぶん、行単位の入力を実現できる...ことだと思います。 - 3 行目に関して、
lines[0]
は、今はこういうおまじないだと思ってください()
(ここら辺の説明は他のサイトをあさってください!!)
3.6.A テンプレートリテラル
これ、自分は初めて知ったのですが、出力に文字列と一緒に変数の値を出したいときに使われる手法が、テンプレートリテラルというものとしてあるようです。
let message = "Hello";
console.log(`${message} World`);
このように書くと、${message}
に "Hello"
が代入されて、そのまま文字列として出力することができるようです。結果は Hello World
となります。
便利ですね...!!
確かに C にも似たような機能があったきがするな...(printf関数)
3.7 条件分岐(if文)
if文!!来ました条件分岐。
これをマスターするだけでも世界が広がって見えます。
次のようなおまじないを書きました。ぴょんっ
let number = 10;
if(number == 10){
console.log("ten!");
}
見慣れた構造ですねぇ...(感動)
解説
if(条件式){...}
と書くと、条件式が成り立つときだけ、そのブロック内のコードが実行されることになります。
上のコードでは、「変数 number
が 10 に一致するか?」という条件です。これはもちろん答えは Yes なので、ten!
という出力がなされます。
3.7.1 else句、else if
上にコードを加えれば、次のようになります。
let number = 1;
if(number == 10){
console.log("ten!");
} else {
console.log("Not ten");
}
(これも見慣れた構造してる...!!)
追加したのは else 句というもので、これは if(条件式)
の条件式が成り立たないときに、実行されるブロックを言います。
上のコードでは、変数 number
の値が 1 です。条件式は変わらず「10 に一致するか?」なので、これは条件が成り立っていません。よって else 句のブロックが実行され、出力は Not ten
が出力されます。
else if というのもあり、これは if(条件式)
が成り立たず、しかし else if(条件式2)
が成り立つときに実行される文となります。
例えば次のコード
let number = 12;
if(number == 10){
console.log("ten!");
} else if(number == 12){
console.log("twelve!!");
} else {
console.log("normal");
}
このコードでは、else if の条件式は「number
の値が 12 に一致するか?」です。
まず if の条件式は成り立ちません。次に else if の条件式を見ますが、これは成り立ちます。よって else if のブロックが実行され、出力は twelve!!
となります。
else if はいくつでも書けますが、書きすぎて、自分が理解に苦しむ、なんてこともありますので、注意が必要です!
3.7.2 条件演算子、論理演算子
来ました。条件分岐に欠かせない演算子、条件演算子および論理演算子!!
参考までに、条件演算子(上のif、else if、elseのコードにおける ==
に当たるもの)と、論理演算子( else if の...代替?)の表を記しておきます。
条件演算子
条件演算子 | 意味 |
---|---|
a == b |
a と b の値が一致するか? |
a < b |
a は b より小さい値か? |
a > b |
a は b より大きい値か? |
a >= b |
a は b 以上か? |
a <= b |
a は b 以下か? |
a != b |
a は b とは異なる値か? |
論理演算子
※条件式を A
、B
とおいています。
論理演算子 | 意味 |
---|---|
A || B |
論理 OR 演算: A か B のうち少なくとも一方が成り立つか? |
A && B |
論理 AND 演算: A と B の両方が成り立つか? |
!(A) |
論理 NOT 演算: A が成り立たないか? |
次のようなコードを実行したとします。
let point = 99;
if(point == 100){
console.log("PASS!!");
} else if(point >= 60){
console.log("pass");
} else {
console.log("failed...");
}
この場合のコードの出力は pass
となります。これは point == 100
が成り立たず、その後 point >= 60
が成り立つためです。
では、point
の値を 59 にして、このとき実行したときの出力を予想してみてください。
答え
failed...
これは、どの条件式も成り立たないためです。
if文については、上から順々に条件が成り立つかどうかを確認します。そして初めて成り立ったときに、そのブロックのコードを実行します。
これは、条件式の順番に注意する必要があることを示しています。例えば以下のコードを実行してみてください。上のコードに対して 2 つの条件式と、pass
、PASS!!
を入れ替えたものです。
let point = 100;
if(point >= 60){
console.log("pass");
} else if(point == 100){
console.log("PASS!!");
} else {
console.log("failed...");
}
期待する出力は PASS!!
とします。ところが実行すると、pass
と表示されます。
これは最初の条件式 point >= 60
が初めに満たしているためです。そのため、それ以降の条件式に対する判定をしなくなってしまいます。
このように、条件式の順番によって出力が左右されることも少なくありません。この点に注意してください。(私は何度もこの経験をして、苦い思い出を何度も経験させられた覚えがあります。。。)
論理演算子を用いれば、上で警告したようなことは起きにくくなります。その反面、実装量が多くなります。以下のコードは警告文に書いてあるコードと同じ意味です。
let point = 100;
if(point != 100 && point >= 60){
console.log("pass");
} else if(point == 100){
console.log("PASS!!");
} else {
console.log("failed...");
}
論理 AND 演算子 &&
を用いてわかりやすく(?)しました。このときの最初の条件式は「point
が 100 ではない、かつ point
が 60 以上であるか?」という意味になり、これなら point
が 100 であったとしても、最初の条件式が成り立たないので、OK となります。
すいません、出力が PASS!!
になります。(大分疲れてきた...)
3.8 ループ文(for文)
そして来ました、プログラミングの真骨頂!!
for文です。これも C++ と同じ、助かります...!!
以下のおまじないで習いました。よんっ
for(let i = 0; i < 10; i++){
console.log(i);
}
これは、1,2,3,...,10 までを出力するプログラムコードです。
解説
-
let i = 0
: これはカウンタ変数i
を定義し、初期値は0
としています。 -
i < 10
: ループを続ける条件式であり、今回は「i
が 10 未満か?」ということになります。 -
i++
: 厳密には複合代入演算子と呼ばれるものです(他言語と機能が同じであれば多分)が、ここではi
を 1 ずつ増やす、という捉え方で構いません。 - ブロック内では、
i
が条件式を満たしている間だけ、処理が繰り返されます。ここではカウンタ変数i
を出力します。
このときの出力結果は
0
1
2
3
4
5
6
7
8
9
となります。もちろん i
も変数の一つなので、このようにfor文のブロック内であれば利用できます。
3.9 標準入力(複数行) -前準備-
...の前に、まず配列を解説をしておきます。プログラミングを、語るにあたって欠かせないものです。
3.9.A 配列
JavaScript も C++ と配列の形は一緒!!異なるのは定義の括弧が []
であるだけ、、、
次のようなコードを実行するとします。
let points = [90, 10, 40];
console.log(points);
console.log(points[0]);
for(let p of points){
console.log(p);
}
- 1 行目: 配列
points
の定義です。3 つの要素 90,10,40 を持った配列を定義しています。この要素を区切るときは,
である必要があります。 - 2 行目 : 配列
points
をまるごと出力します。このときの出力は[ 90, 10, 40 ]
となります。 - 3 行目 : 配列の、1 番目を参照しています。配列の番目(インデックス、添え字)は 0 始まりであることに注意が必要です。
- 5 行目以降:
for(let (変数名) of (配列名)){...}
において、(変数名) に配列の要素が次から次へと代入されます。よって、全要素を列挙することができます。
この実行結果は以下のようになります。
[ 90, 10, 40 ]
90
90
10
40
見づらいですが、3 行目以降 90 10 40 で、配列の要素を列挙できていますね!
3.9 標準入力(複数行)
以前書いた標準入力のコードを再掲します。
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let message = lines[0];
console.log(message);
3 行目、lines[0]
が、気になりますね...!?!?!?
そうです、配列の知識を用いれば、2 行目は lines[1]
、3 行目は lines[2]
、...、として格納されるのです。
これを用いて、for 文などを駆使すれば、
無双できます(確信
例えば次のコードを書いたとします。
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let count = lines[0];
for(let i = 0; i < count; i++){
let message = lines[i+1];
console.log(message);
}
- 3 行目 : 1 行目の入力
lines[0]
を、変数count
に格納します。これは、入力される文字列の個数を表します。 - 5 行目 : $i$ 行目の入力
lines[i+1]
を、変数message
に格納します。これは、入力される文字列を表します。6 行目でその文字列を出力します。
変数 message
を省略して、そのまま console.log(lines[i+1])
でも構いません。
例えば次の入力をしてみます。
3
Isiusu
Janakute
Ishiusu
このときの出力は次のようになります。
Isiusu
Janakute
Ishiusu
:
以上です!!
:
:
はい!
以上てな感じでございました。
えー、ここから、paiza様のサイトで実際に作成された問題を元に理解を深めていくコーナーとなって、いきます.......(疲れた)
一休みゾーンです。ここは。お茶をふんだんにおいておきます。
🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵🍵✨
4. paizaさんのB,C,Dランク問題で腕試し
今回は体験程度なので $4$ 問だけ解きました。早速、解いていきます!!
4.1 足し算(Dランク)
問題概要は以下です。
整数 $A,B$ が半角空白区切りで与えられます。
$A+B$ の結果を出力してください。
(原文はこちら)
THE・典型問題!!
ですが、私が昔も今も恐れていたことがあり、先述した通り JacaScript の入力は一行単位なんですよね...
つまり、半角空白区切りだとしても、JavaScript では A B
という文字列として入力されてしまうということ!!
ここが味噌でした。八丁味噌。
私の解答は以下のようになります。
解答
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let datas = lines[0].split(" ");
let a = parseInt(datas[0]);
let b = parseInt(datas[1]);
console.log(a+b);
解説とコメント
ここで躓いたのは、やはり半角空白区切りの処理ですね...
調べたところ、split 関数は「何かを基準にして区切り、配列に直す」という処理をするようです。
で、その区切った要素をそのまま足し算にする、というのも、間違いでした...
整数型に直さなければならないようです(これだから動的型付けは)。
そして調べたところ、parseInt 関数というものがあり、これは整数に直す関数らしいです。これは、使える...?
使えました。できました。提出しました。正解しました!!!
4.2 一番小さい値(Dランク)
問題概要は以下です。
$5$ つの整数 $n_1,n_2,n_3,n_4,n_5$ が改行区切りで与えられます。
この整数のうち、最小のものを出力してください。
(原文はこちら)
「改行区切り」
この言葉は、いつまでたっても、恩恵を受けたことを忘れることができませんでした(?)。
つまり、普段通り lines[i]
でそのまま受け取れるということです。
私の解答は以下のようになります。
解答
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let ans = 100; //答えとなる値
for(let i = 0; i < 5; i++){
let num = parseInt(lines[i]);
if(num < ans) ans = num; //最小値の更新
}
console.log(ans);
解説とコメント
また parseInt に惑わされました。この子無いと生きていけないのかもしれません。
最小値は、今答えとなる値より、入力した値が小さい場合は。答えを更新する、といった手続きを行うことで判定できました。
JavaScript の入力値は、parseInt を用いて整数に直す!!
さぁ、次は少し難易度を上げて解いてみます。
4.3 みかんの仕分け(Cランク)
問題概要は以下です。
$M$ 個のみかんがあり、それぞれ重さが $w_1,w_2,\dots,w_M$ でした。
正の定数 $N$ が与えられるので、この整数倍すなわち $N,2N,3N,\dots$ の書かれた箱があり、それぞれのみかんに対して、箱に書かれた整数とより近い方の箱に入れたいです。
ただし整数倍といっても $0N=0$ の書かれた箱には入れられず、ある $2$ つの箱に書かれた整数と、みかんの重さの差が同じなら、大きい整数が書かれた箱を採用します。
このとき各みかんに対して、何の整数が書かれた箱に入れるか出力してください。
(原文はこちら)
わぁお、ちょっと問題が複雑になりました。
しかし私は3年以上の競プロ経験を持っているので、余裕で~す♪
全然余裕じゃありませんでした。
私の解答は以下になります。
解答
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let data = lines[0].split(" ");
let N = parseInt(data[0]);
let M = parseInt(data[1]);
let W = [];
for(let i = 0; i < M; i++){
let w = parseInt(lines[i+1]);
W.push(w);
}
for(let w of W){
let p = parseInt(w/N);
let r = w%N;
if(r >= parseInt(Math.ceil(N/2)) || p == 0) p++;
console.log(p*N);
}
解説とコメント
入力は、いつも通り、半角空白区切りに対応させています。
W.push(w)
と書きましたが、これは「配列 W
に変数 w
を、配列の末尾に挿入する」という関数を言います。
で、肝心の解答部分ですね...これは。
自分はこんな方針で解きました。
- より数値が、$N$ の整数倍に近い -> これは割り算で候補 $p$ が絞れるな...
- で、さらに割り算のあまり $r$ を持っておいて、これを使ってどの箱に入れるかを判定する
- $N=10$ のときを考えると、あまりが $5$ 以上のときは $p+1$ の方が近いし、$r=5$ のときは大きい方を採用するので、この考え方はあってるはず...
- $p=0$ のときは $p+1$ にするしかないので合ってる
- でも、$N$ に関してどうやって判定すれば... -> 中間である $\frac{N}{2}$ を基準にすればいいのか!
- 最後に $pN$ が答えになるな
といった考えでした。
ここで罠だったのは、parseInt は整数に直す、ということなので、切り上げの機能が無い、ということでした。私はこのコードを落とすケースで 1 回不正解 (80点) になりました...
そこで切り上げの関数を調べてみると、Math.ceil 関数というものがありました。
使いました。提出しました。正解しました!!
ではいよいよ、Bランクの問題を解きたいと思います。
4.4 長テーブルのうなぎ屋(Bランク)
問題概要は以下です。
$n$ 個の席があり、席は隣り合っています。席 $n$ の右隣は席 $1$ です。
$m$ グループのお客さんが入ってきました。$i$ グループ目では、$a_i$ 人のお客さんが、席 $b_i$ を始点として席 $b_i,b_i+1,b_i+2,\dots$ と連続する席に座ろうとしています。
しかし座ろうとしている席に $1$ 席でも先客がいる場合は、$i$ グループ目のお客さんは全員帰ってしまいます。
最後のグループの対応を終えた後、無事にお客さんが座れている席はいくつあるか出力してください。
(原文はこちら)
あぁ...おやすみなさい。むずそうです^^、むずかしかったです^^
私の解答は以下のようになります。
解答
let lines = require("fs").readFileSync("/dev/stdin","utf8").split("\n");
let data = lines[0].split(" ");
let N = parseInt(data[0]);
let M = parseInt(data[1]);
let seen = []; //i番目の席は空いているか
for(let i = 0; i < N; i++) seen.push(0);
for(let i = 0; i < M; i++){
let data2 = lines[i+1].split(" ");
let a = parseInt(data2[0]);
let b = parseInt(data2[1]);
b--; //インデックスを 0 始まりに直す
//全員座れるか判定
let ok = 1; //全員座れるかどうか(最初は座れると仮定)
for(let j = 0; j < a; j++){
let idx = (j+b)%N;
if(seen[idx] == 1) ok = 0;
}
//console.log(ok);
if(ok == 1){
for(let j = 0; j < a; j++){
let idx = (j+b)%N;
seen[idx] = 1;
}
}
}
let ans = 0;
//for(let i = 0; i < N; i++) console.log(seen[i]);
for(let i = 0; i < N; i++) if(seen[i] == 1) ans++;
console.log(ans);
解説とコメント
2 行目で、入力 1 行目の 1 行入力を受け取り、それぞ split 関数で空白で区切って配列に直す。
3,4 行目で区切った配列から、それぞれ $N,M$ を表す変数 N
、M
を作る。
ここまでは前とほぼ同じですが、ここからです。
まず、席 $i$ が埋まっているかどうかを表す配列を作らなければなりません。7,8 行目で、これを実装しています。ここで、seen[i] = 0
は「席 $i$ が空いている」、seen[i] = 1
は「席 $i$ が埋まっている」ことを表すとします。
ここから、$M$ 行の入力を受け取りつつ問題の答えを求めていきます。
1. 連続する a 席が空いているかどうか判定する
まず、$b$ ですが、配列のインデックスの始まりは $0$ からでした。つまり、$b$ から $1$ 引いて、seen[b+j] = 0
が $j=0,1,\dots,a-1$ に対して成り立つかどうかを判定する必要があります。このときのインデックス idx
ですが、(j+b)%N
としています。これは、席 $N$ の隣が席 $1$ であるためであり、例えば以下のように変えても構いません。(こちらの方が見やすくなります)
let idx = j+b;
if(idx >= N) idx -= N;
実はこれも (j+b)%N
の役割を果たしています。(説明は省略しますが、$a\div b$ の商 $q$ と剰余 $r$ の関係式 $a=bq+r$ から説明することが可能です)
2. 全席空いていたなら、連続する a 席を埋める
1.では「seen[b+j] = 0
が $j=0,1,\dots,a-1$ に対して成り立つかどうかを判定する」と言いました。これを、実際に seen[b+j] = 1
にする動作を行います。
3. 席が埋まっている個数を数える -> 答えを出力
これは単に for 文を用いて seen[i] = 1
となる $i$ の個数を数えればよいです。
こうして答えを出力すれば、正解できます!!
5. JavaScript を学びなおしてみた感想
今回は JavaScript を学びなおしてみました。感想としましては、初心を思い出せたような気持ちで、新鮮な感情を体験しました。
私も昔は、まったくと言っていいほどプログラミングに対しては無関心でした。しかしいざやってみると、コードを自分で書くようになったといった楽しさや、自分の作りたいプログラムが書けるようになったことの幸せが感じられました。
今は、IT人員もまだまだ少ない時代。
"プログラミング"を先取ることができた私は、今まで以上の幸福をつかみ取ることができたと心から思っています。
また、プログラミングを通じて知り合える世界も広がって見えて、人生を楽しむ理由の一つになったとも思えます。
そんな幸福がまた味わえたような気がしました。
まだまだ知らない言語や、知らないITの世界があるので、その分野も探求してみたいと思います。
時間さえあれば!!!!!!!!!
6. Eplilogue
ずっと JavaScript はほとんど Java みたいなもんだろ!!と思ってたけど
全然違いました(そりゃそう)
極めて来ます。ネットワーク関係でも使うし...!!!
では、またどこかでお会いしましょう。
:
End.✨🗻