プログラミング初心者修行シリーズ第三弾。
本日はJavaを勉強し初めてちょうど2ヶ月記念日ということで、paizaラーニングで提供されている問題に挑戦してみました。2ヶ月前は単発のfor文がギリギリかけるくらいで、型変換すらできない実力でした。今回はどれだけ成長できたかを確認したかったためD,Cランクを飛び越えてBランクの過去問題に挑戦してみました!!
今回は「paizaラーニング(スキルチェック過去問題集)」より出題された「カブトムシの誘導(Bランク相当)」になります。
※ちなみに、paizaラーニングでは過去問題のみコード公開が可能なのでご注意ください。
問題
<問題>
与えられた命令にしたがってライトを点灯させた時、3 匹のカブトムシが同じ位置に集まることがある場合、それらが最初に集まるマスの番号を整数で出力してください。
同じ位置に集まることがない場合、”no” と出力してください。
<条件>
①3 匹のカブトムシは左右にまっすぐ伸びる道の上にいて、
道は 1 マスごとに区切られていて、それぞれのマスは順に …, -2, -1, 0, 1, 2, … と番号づけられているものとします。
②ライトは赤、緑、青およびこれらを組み合わせてできる色 (赤、緑、青、黄、マゼンタ、シアン、白の 7 種類) で点灯させることができます。
③ライトが点灯した時、3 匹のカブトムシはそれぞれ次のような動きをします。
・赤のカブトムシは、赤色を含む光 (すなわち、赤、黄、マゼンタ、白のいずれか) で照らされた時、ライトのある方向に 1 マス分移動する。
・緑のカブトムシは、緑色を含む光 (すなわち、緑、黄、シアン、白のいずれか) で照らされた時、ライトのある方向に 1 マス分移動する。
・青のカブトムシは、青色を含む光 (すなわち、青、マゼンタ、シアン、白のいずれか) で照らされた時、ライトのある方向に 1 マス分移動する。
④3 匹のカブトムシの初期位置および「左右どちらかのライトをある色で点灯させる」という命令の列が与えられます。
入力される値は、下記入力例と同じフォーマットで与えられます。
(入力例1)
1行目=命令回数、
2行目=赤緑青カブトムシの初期位置、
3行目以降=「ライトの位置 ライトの色」
3
1 2 3
R Y
L C
L R
解答コード
模範解答がないため正解のコードはわかりませんが、私が正解できたごりおしコードで紹介します。
ちなみに入力には適当な入力例を入れており、その場合の出力は7になっています。
↓下記ソースコードは「実行(Ctrl-Enter)」のすぐ下のグレーの線あたりを少し長押しして上げ下げするとソースコード画面が大きく開き見安くなります。
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
String line2 = sc.nextLine();
//変数lineNumに命令回数を格納する
Integer lineNum = Integer.valueOf(line);
//配列conditionsに移動条件「○ ○」を格納する
String []conditions = new String [lineNum];
for(int i=0; i<lineNum; i++){
conditions [i] = sc.nextLine();
}
//配列positionに各カブトムシの位置を格納する
String [] position = line2.split(" ");
//String型の数値からInteger型の数値にそれぞれ型変換する
Integer positionRed = Integer.valueOf(position[0]);
Integer positionGreen = Integer.valueOf(position[1]);
Integer positionBlue = Integer.valueOf(position[2]);
//正規表現を用いて場合分けを行う
loop1:
for(int i=0; i<lineNum; i++){
//①「右ライト」の場合
if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "Y" + ".*"))){
positionRed += 1;
positionGreen += 1;
}else if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "C" + ".*"))){
positionGreen += 1;
positionBlue += 1;
}else if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "M" + ".*"))){
positionRed += 1;
positionBlue += 1;
}else if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "W" + ".*"))){
positionRed += 1;
positionGreen += 1;
positionBlue += 1;
}else if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "B" + ".*"))){
positionBlue += 1;
}else if((conditions [i].matches(".*" + "R" + ".*")) && (conditions [i].matches(".*" + "G" + ".*"))){
positionGreen += 1;
//②「左ライト」の場合
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "Y" + ".*"))){
positionRed -= 1;
positionGreen -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "C" + ".*"))){
positionGreen -= 1;
positionBlue -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "M" + ".*"))){
positionRed -= 1;
positionBlue -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "W" + ".*"))){
positionRed -= 1;
positionGreen -= 1;
positionBlue -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "R" + ".*"))){
positionRed -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "G" + ".*"))){
positionGreen -= 1;
}else if((conditions [i].matches(".*" + "L" + ".*")) && (conditions [i].matches(".*" + "B" + ".*"))){
positionBlue -= 1;
//③「右ライト」かつ「赤色」の場合("R"のみの部分一致)
}else{
positionRed += 1;
}
//同じ位置の時その位置を表示し、for文を抜ける
if((positionRed == positionGreen) && (positionGreen == positionBlue)){
System.out.println(positionRed);
break loop1 ;
}
}
//最終的な位置が異なった場合「no」を表示する
if(!(positionRed == positionGreen) || !(positionGreen == positionBlue) || !(positionRed == positionBlue)){
System.out.println("no");
}
}
}
ではこれだけだとどんな処理なのかわかりづらいと思うので私なりの考え方を説明していきます。
考え方としては、それぞれの初期位置に処理によって+ーされることで各カブトムシの位置が一致していくのかを確認しています。
①7行目で命令回数を変数lineに格納
②8行目で各カブトムシの初期位置を変数line2に格納
③11行目で命令回数をInteger型に型変換して変数lineNumに格納
④14行目〜17行目で条件である「ライトの位置」「ライトの色」情報を配列conditionsに格納(この時配列conditionsには「◯ 空白 ◯」の形でそれぞれ命令回数分格納されている。)
⑤20行目各カブトムシの初期位置を示す値を空白区切りで配列positionに格納
⑥23〜25行目で各カブトムシの初期位置を示す値をString型からInteger型に型変換
⑦28行目から「右ライト点灯の場合」と「左ライト点灯の場合」とで場合分けを行う
今回は、配列conditionsの各インデックスには「ライトの位置」と「ライトの色」を示す値が格納されているので、右ライトを示す「R」と左ライトを示す「L」どちらの文字が部分一致、かつ、ライトの色を示す「R・G・B・Y・M・C・W」7パターンと文字が部分一致している場合の条件を正規表現を用いて記述
⑧「右ライト」で「赤ライト」の時だけ「R R」となってしまい途中で部分一致を行うとおかしくなってしまうので最後に記述
⑨問題文より、最初に集まる位置を出力しないといけないので、76行目〜79行目では入力された命令回数未満で位置が一致した場合、その位置を出力した後ループ文を抜ける。(loop1ラベルを使用することでfor文を抜け出すことが出来る。)
⑩82行目〜84行目では命令回数分処理を繰り返しても位置が一致しなかった場合、「no」を出力する
学んだコード
プログラムに渡される値やデータの標準(デフォルト)の入力元のこと。ただしファイルからのデータの読み込みの場合は、
standard input (標準入力)→「stdin」と略されて使用されることが多い
・ラベル:外側のループ文を抜けたい時に使用(今回はbreakで使用)。continueなども使用できる。
・splitメソッド:正規表現にマッチした位置で文字列を分割し、分割した結果を文字列配列として返します。引数に単なる文字(列)を指定した場合には、一致した位置で文字列を分割する。
最後に
制限時間は5時間という中、本来なら数十分で解きたいところを2時間半もかけてしまいました。
今回時間がかかってしまったのは問題文の理解と、どうしたら処理できる形に持っていけるかというところでした。あとは、与えられた必要な数値を分割して使用したり、場合分けを考える部分でも苦戦してしまいました。
ですが、一から自分の力だけでやれたことと、以前では絶対に達成できなかったであろう問題を解けたことが今回の大きな収穫と思っています。
これから勉強を続けていく上で、成長できているという実感を持つことがモチベにも繋がるので今回は時間はかかりましたが確認できてよかったなと感じました。
では、また👋