AtCoder振り返り168(A~D)

2020年5月17日開催のAtCoder参加結果を振り返ります。

結果

結果はA, B, Cの3問正解の3完です。
3問目までは25分程度で解けましたし一発ACでしたがD問で阻まれてしまいました。
D問は問題内容を間違って理解しており解けませんでした。(後述)

成績

成績は若干上昇しています。
あと1回挑戦すれば茶色コーダにはなれるでしょうか?
f:id:lawrence-twin:20200518214449p:plain

順位は以下の通り
うーんこんなものですかね。
f:id:lawrence-twin:20200518214656p:plain

問題

問1 ∴ (Therefore)


引数Nの1の位がいずれかによって出力を分ける問題

  • パターン1:2,4,5,7,9 のとき :hon
  • パターン2:0,1,6,8のとき :pon
  • パターン3:それ以外の時 :bon

もっと簡単にできたと思うのですが、愚直に実装しました。
解説ではswitch分で簡潔に記載していますね。

回答コード

import java.util.Scanner;
 
public class Main {
	public static void main(String[] args) {
		try (Scanner scn = new Scanner(System.in)) {
			String N = scn.next();
			String c = N.substring(N.length()-1);
 
			if(c.equals("2")
			|| c.equals("4")
			|| c.equals("5")
			|| c.equals("7")
			|| c.equals("9")
			) {
				System.out.println("hon");
			} else if(c.equals("0")
					|| c.equals("1")
					|| c.equals("6")
					|| c.equals("8"))
			{
					System.out.println("pon");
			} else {
				System.out.println("bon");
			}
		}
	}
}

問2 ... (Triple Dots)

以下の2パターンの実装を行います。

  • パターン1:引数SがKを超える場合:K文字出力 + "..."
  • パターン2:それ以外の場合:そのまま引数Sを出力

文字列操作さえできれば特に難しくはないと思います。

回答コード

import java.util.Scanner;
 
public class Main {
	public static void main(String[] args) {
		try (Scanner scn = new Scanner(System.in)) {
			int K = scn.nextInt();
			String S = scn.next();
 
			if(S.length() <= K) {
				System.out.println(S);
			} else {
				System.out.println(S.substring(0, K) + "...");
			}
 
		}
	}
}

問3

時計の長針と短針の先端を結ぶ直線の長さを求める問題です。

引数として渡される情報は以下の通りです。
引数A:短針(時針)の長さ
引数B:長針(分針)の長さ
引数H:時
引数M:分

三角形が仮に直角三角形だった場合、
短針の長さ × 短針の長さ + 長身の長さ × 長身の長さ = √直線の長さ となります。
これが三平方の定理です。

しかし、時刻によっては三角形は直角になりません。(というかほとんどの場合直角三角形にはなりません。)
そうすると三平方の定理では求められません。これを求める方法として「余弦定理」を使います。
余弦定理を使うためには角度の計算が必要ですが、時・分の長さから求められます。

短針(時針)の1分で進む角度:0.5℃
長針(分針)の1分で進む角度:6℃

コードに落とし込むと以下の通りとなりました。

回答コード

import java.util.Scanner;
 
public class Main {
	public static void main(String[] args) {
		try (Scanner scn = new Scanner(System.in)) {
			int A = scn.nextInt();
			int B = scn.nextInt();
			int H = scn.nextInt();
			int M = scn.nextInt();
 
			double longAngle = M * 6;
			double shortAngle = H * (360/12) + M * 0.5;
 
			double angle = 0;
 
			if(longAngle < shortAngle) {
				longAngle = 360 + longAngle;
				angle = longAngle - shortAngle;
			} else {
				angle = longAngle - shortAngle;
			}
 
			//余弦定理
			System.out.println(Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2) -2*A*B*Math.cos(Math.toRadians(angle))));
		}
	}
}

問4 .. (Double Dots)

ある部屋から最短で部屋1にたどり着くために、
各部屋に道しるべ(進むべき部屋の番号)を求めるという問題です。

解説にある通り、部屋1の深さを0として考えた場合以下の重要な事実が成り立つとのこと
「深さ d + 1 の部屋には,深さ d の部屋が少なくとも 1 つ繋がっている.」

以下のようにMap機能を利用したうえで、
各部屋ごとに幅優先探索で深さを調べていけばいずれ分かります。

import java.util.*;
 
public class Main { 
    public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		Map<Integer, List<Integer>> map = new HashMap<>();
 
		for(int i=0; i<m; i++){
			int a = sc.nextInt();
			int b = sc.nextInt();
			List<Integer> value = map.get(a);
			if(value == null) value = new ArrayList<>();
			value.add(b);
			map.put(a, value);
			List<Integer> value2 = map.get(b);
			if(value2 == null) value2 = new ArrayList<>();
			value2.add(a);
			map.put(b, value2);
		}
		int[] ans = new int[n+1];
		ArrayDeque<Integer> dq = new ArrayDeque<>();
		dq.add(1);
		while(dq.size() > 0){
			Integer i = dq.removeFirst();
			List<Integer> value = map.get(i);
			for(Integer j: value){
				if(ans[j] == 0){
					ans[j] = i;
					dq.add(j);
				}
			}
		}
		System.out.println("Yes");
		for(int i=2; i<=n; i++){
			System.out.println(ans[i]);
		}
	}
}


解説
https://img.atcoder.jp/abc168/editorial.pdf