diff --git a/ntakanabe/src/KaraokeTournament.java b/ntakanabe/src/KaraokeTournament.java new file mode 100644 index 0000000000000000000000000000000000000000..a55c14bd9781841eb4b7e2ecccff258e60bd4020 --- /dev/null +++ b/ntakanabe/src/KaraokeTournament.java @@ -0,0 +1,194 @@ +package paiza.src; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +/** + * B095 カラオケ大会の得点計算をするクラス. 課題曲の正しい音程とN人の参加者が歌った音程が与えられ、 各音程の誤差に応じて減点し、N人の中での最高得点を出力する. + * + * @author ntakanabe + */ +public class KaraokeTournament { + + // カラオケの初期得点 + private static final int INITIAL_SCORE = 100; + + /** + * 音程の誤差に応じた減点数を定義するEnum. + */ + private enum Penalty { + + // 誤差範囲(第1、第2引数の範囲)と減点数(第3引数)を紐付け + PERFECT(0, 5, 0), + GREAT(6, 10, 1), + GOOD(11, 20, 2), + NORMAL(21, 30, 3), + BAD(31, Integer.MAX_VALUE, 5); + + private final int minError; + private final int maxError; + private final int penaltyPoints; + + Penalty(final int minError, final int maxError, final int penaltyPoints) { + this.minError = minError; + this.maxError = maxError; + this.penaltyPoints = penaltyPoints; + } + + /** + * 指定された誤差に対する減点数を返すメソッド. + * + * @param error 音程の誤差 + * @return 減点する点数 + */ + public static int getPenaltyByError(final int error) { + + for (final Penalty p : Penalty.values()) { + if (error >= p.minError && error <= p.maxError) { + return p.penaltyPoints; + } + } + // ここには基本到達しない(念のため) + return 0; + } + } + + /** + * 標準入力から入力値(参加人数、曲の長さ、正しい音程、参加者の歌唱音程)を読み込むクラス. + */ + public static class LoadParameter { + private int singerCount; + private int songLength; + private List correctPitches; + private List allSingerPitches; + + /** + * コンストラクタ. + */ + public LoadParameter() { + + final Scanner sc = new Scanner(System.in); + + this.singerCount = sc.nextInt(); + this.songLength = sc.nextInt(); + + this.correctPitches = new ArrayList<>(); + for (int i = 0; i < this.songLength; i++) { + this.correctPitches.add(sc.nextInt()); + } + + this.allSingerPitches = new ArrayList<>(); + for (int i = 0; i < this.singerCount; i++) { + for (int j = 0; j < this.songLength; j++) { + this.allSingerPitches.add(sc.nextInt()); + } + } + + sc.close(); + } + + public int getSingerCount() { + + return singerCount; + } + + public int getSongLength() { + + return songLength; + } + + public List getCorrectPitches() { + + return correctPitches; + } + + public List getAllSingerPitches() { + + return allSingerPitches; + } + } + + /** + * 入力メソッドから入力値を受け取り、最高得点を計算するメソッドで処理をし、その結果を出力メソッドに渡すmainメソッド. + * + * @param args コマンドライン引数 + */ + public static void main(final String[] args) { + + final LoadParameter loadParameter = new LoadParameter(); + final int singerCount = loadParameter.getSingerCount(); + final int songLength = loadParameter.getSongLength(); + final List correctPitches = loadParameter.getCorrectPitches(); + final List allSingerPitches = loadParameter.getAllSingerPitches(); + + final int maxScore = calculateMaxScore(singerCount, songLength, correctPitches, + allSingerPitches); + + outputResult(maxScore); + } + + /** + * カラオケ参加者の得点を個別に計算し、最高得点を選出する処理メソッド. + * + * @param singerCount 参加人数 + * @param songLength 課題曲の長さ + * @param correctPitches 課題曲の正しい音程のリスト + * @param allSingerPitches 大会に参加したすべての人が歌った音程のリスト + * @return singerCount人の中での最高得点 + */ + private static int calculateMaxScore(final int singerCount, final int songLength, + final List correctPitches, final List allSingerPitches) { + + int maxScore = -1; + + // 各参加者の歌唱音程を抽出 + for (int i = 0; i < singerCount; i++) { + final List sungPitches = new ArrayList<>( + allSingerPitches.subList(i * songLength, (i + 1) * songLength)); + + // プレイヤーごとの得点計算を別のメソッドに委譲 + final int singerScore = calculateSingerScore(songLength, correctPitches, sungPitches); + + maxScore = Math.max(maxScore, singerScore); + } + return maxScore; + } + + /** + * 一人のプレイヤーのカラオケ得点を計算するメソッド. 採点は100点からの減点方式で、音程の誤差に応じて点数が引かれ、0点を下回ることはない. + * + * @param songLength 曲の長さ + * @param correctPitches 曲の正しい音程のリスト + * @param sungPitches 現在のプレイヤーが歌った音程のリスト + * @return そのプレイヤーの最終得点 + */ + private static int calculateSingerScore(final int songLength, + final List correctPitches, + final List sungPitches) { + + int singerScore = INITIAL_SCORE; + + for (int i = 0; i < songLength; i++) { + final int sungPitch = sungPitches.get(i); + final int error = Math.abs(correctPitches.get(i) - sungPitch); + + singerScore -= Penalty.getPenaltyByError(error); + + if (singerScore < 0) { + singerScore = 0; + } + } + return singerScore; + } + + /** + * 計算された最高得点を出力するメソッド. + * + * @param maxScore 最高得点 + */ + private static void outputResult(final int maxScore) { + + System.out.println(maxScore); + } +}