※説明を加えました(2022/07/07)
はじめに
初めてABCが開催されたときの問題ということもあり、メモリ制限が若干厳しめです。あと、今の感覚でDifficulty見るとそんな高い?って感じるかもしれませんが、今のABCの難易度が上がっているだけです。正常な感覚です。
では、実際に私がACを取れたコードを見ながら解説していきます。
A-積雪深差
問題文はこちら
単純にH1-H2を行なえば良いですね。
Scannerクラスを使えば整数を一つずつ受け取れるのでこれを使ってH1、H2を受け取りましょう
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//二つの整数を受け取る
int H1 = sc.nextInt();
int H2 = sc.nextInt();
//差を出力
System.out.println(H1-H2);
}
}
今のABCもこれくらい優しいと助かるんですがね。
B-視程の通報
問題文はこちら
ちょっと条件が多いですが、そのままコードに落とし込んでやりましょう。
※このコードはprintfを使わない書き方です。printfの方が簡潔に書けるので、そちらを用いても良いでしょう。
○○より小さいかifで調べれば、それ以降のelse ifではわざわざ「○○より大きくて△△より小さいとき」という書き方はしなくて済みます。ということで、小さい値から調べて行きましょう。
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//整数を受け取る
int m = sc.nextInt();
//出力用に文字列に変換しておく(しなくてもいい)
String mm = String.valueOf(m);
//100未満は「00」を出力
if(m<100)
System.out.println("00");
//1000未満だとmの上1桁に「0」を先頭に付けたものを出力
else if(m<1000)
System.out.println("0" + mm.substring(0,1));
//1000以上5000以下ならそのままmの上2桁を出力
else if(m<=5000)
System.out.println(mm.substring(0,2));
//5000より大きくて30000以下なら上二桁+50を出力
else if(m<=30000)
System.out.println(m/1000+50); //+50がやりやすいので「m/1000」を使用
//30000より大きくて70000以下なら上二桁-30を5で割って80を足したものを出力
else if(m<=70000)
System.out.println((m/1000-30)/5+80);
//他は「89」を出力
else
System.out.println("89");
}
}
注意点を挙げるとすれば、条件が<
か<=
に気をつけましょう。
この微妙な違いで私はWAを量産しました。
C-風力観測
問題文はこちら
とんでもない分岐の数ですが、全部書き出せばどうにかなります。
こちらもBと同様、条件文周りに気をつけましょう。
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//値を受け取る
int Deg = sc.nextInt();
double Dis = sc.nextDouble();
//値を前処理(条件が見やすいため)
double ms = Math.round(Dis/6.0) / 10.0;
//風速が弱いなら「C 0」を出力して終了
if(ms<=0.2){
System.out.println("C 0");
System.exit(0);
}
//風向きの条件を元に全条件を記述する
if(Deg<=112) System.out.print("N ");
else if(Deg<=337) System.out.print("NNE ");
else if(Deg<=562) System.out.print("NE ");
else if(Deg<=787) System.out.print("ENE ");
else if(Deg<=1012) System.out.print("E ");
else if(Deg<=1237) System.out.print("ESE ");
else if(Deg<=1462) System.out.print("SE ");
else if(Deg<=1687) System.out.print("SSE ");
else if(Deg<=1912) System.out.print("S ");
else if(Deg<=2137) System.out.print("SSW ");
else if(Deg<=2362) System.out.print("SW ");
else if(Deg<=2587) System.out.print("WSW ");
else if(Deg<=2812) System.out.print("W ");
else if(Deg<=3037) System.out.print("WNW ");
else if(Deg<=3262) System.out.print("NW ");
else if(Deg<=3487) System.out.print("NNW ");
else System.out.print("N ");
//風速の条件を元に全条件を記述する
if(ms<=1.5) System.out.println(1);
else if(ms<=3.3) System.out.println(2);
else if(ms<=5.4) System.out.println(3);
else if(ms<=7.9) System.out.println(4);
else if(ms<=10.7) System.out.println(5);
else if(ms<=13.8) System.out.println(6);
else if(ms<=17.1) System.out.println(7);
else if(ms<=20.7) System.out.println(8);
else if(ms<=24.4) System.out.println(9);
else if(ms<=28.4) System.out.println(10);
else if(ms<=32.6) System.out.println(11);
else System.out.println(12);
}
}
とんでもない分岐数だ・・・。
最初の方角の情報は改行しないためにprintメソッドを使いましょう。
D-感雨時刻の整理
問題文はこちら
一気に難易度が上がったような印象です。
単純にやろうにもどうすれば・・・?って感じですが、入力の制約に注目するとNは30000以下であると書いてあります。したがって、boolean型の長さ2402(範囲外参照回避のために2だけ多くなってます)の配列に対して全部のメモを反映させても最大で7.2×10^7回の計算で済むので十分終わりますね。ということで、メモ一つ一つをそのままboolean型配列に反映させて(false→true)最後にtrueで繋がっているところを出力していけばACとれます。
import java.util.*;
class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//Nを受け取る
int N = sc.nextInt();
//メモを受け取る可変長配列を準備(32bit配列を格納)
ArrayList<Integer[]> memo = new ArrayList<Integer[]>();
//メモを受け取るためのforループ
for(int i=0;i<N;i++){
//文字列として受け取って「-」で分ける
String[] temp = sc.next().split("-");
//配列に入れ物を準備
memo.add(new Integer[2]);
memo.get(i)[0] = Integer.parseInt(temp[0]);
memo.get(i)[1] = Integer.parseInt(temp[1]);
}
//各時間をまるめる
marumeruzo(memo);
//降ってる時間を記録するためのboolean型配列を準備(以下bool)
boolean[] isRaining = new boolean[2402];
//メモを元にboolに記録
for(int i=0;i<N;i++){
for(int j=memo.get(i)[0];j<=memo.get(i)[1];j++){
isRaining[j] = true;
}
}
//直前まで降っていたかのboolean型変数、降り始めの時刻を記録する変数を準備
boolean sakkimadehutteta = false;
int fir = -1;
//boolをforループで全部見ていく
for(int i=0;i<isRaining.length;i++){
//今降ってる?
if(isRaining[i]){
//さっきも降ってた?
if(sakkimadehutteta) continue;
//降り始めならその情報を記録
else{
fir = i;
sakkimadehutteta = true;
}
}
//今降っていない時の処理
else{
//直前まで降ってた?
if(sakkimadehutteta){
//文字列の長さ合わせ
for(int j=0;j<4-String.valueOf(fir).length();j++) System.out.print(0);
//最初の時間と間の「-」を出力
System.out.print(fir+"-");
//文字列の長さ合わせ
for(int j=0;j<4-String.valueOf(i).length();j++) System.out.print(0);
//一分前まで降っていたと出力
System.out.println(i-1);
//降らなくなったことを記録
sakkimadehutteta = false;
}
}
}
}
//まるめ用メソッド
public static void marumeruzo(ArrayList<Integer[]> memo){
for(int i=0;i<memo.size();i++){
//処理用に配列を準備
Integer[] temp = memo.get(i);
//端数合わせ用変数
int kuriage = 0;
if(temp[1]%5!=0)
kuriage = 5;
//5で割ることで商のみにできる(例 (11/5)*5=10)
temp[0] = (temp[0]/5)*5;
temp[1] = (temp[1]/5)*5+kuriage;
//分のところが1時間超えてる?
if(temp[1]%100==60)
temp[1] = (temp[1]/100+1)*100; //60分を1時間に変換
//元の配列に戻す
memo.set(i,temp);
}
}
}
WA、TLE、RE、MLEのオンパレードでダメージが大きくて変数名とメソッド名がところどころ遊んじゃってますが許してください。
総評
それぞれの私の感想は
A:優しい。助かる
B:条件が多い点を除けば若干優しい。
C:もっと条件分岐がめんどくさい。大変。
D:単純に解くとMLE+TLEに襲われる。難易度に納得。
って感じです。茶色ならDまで解けるようになると安心って感じですかね。茶色の私が言えたことではないですが・・・。