문제 링크 - 백준 : 학생별 통화 요금 계산

해시맵을 이용한 데이터 집계

문제 설명

  1. 학생들의 통화 기록이 N개 주어진다.
  2. 같은 학생의 통화 시간은 누적된다.
  3. 누적된 시간을 기준으로 요금을 계산한다.
    1. 기본 100분까지 10원
    2. 초과 시 50분당 3원 (올림)
  4. 요금이 많은 순서로, 요금이 같다면 이름 사전(오름차)순 출력

접근

데이터를 집계해야 하므로 HashMap을 사용해보았다. Rust에서는 entry 를 사용하면 키가 없을 때 초기화하고 값을 더하는 로직을 편하게 적용할 수 있음.

시간은 HH:MM 형태로 주어지니 미리 분으로 바꿔버리는게 편함.

정렬 조건이 두 가지라는 부분이 머리를 복잡하게 만들지만

Rust 의 경우 cmpOrdering 사용해서 해보았다.

JavaScript는 조건문으로..

정답

Rust

use std::{
    cmp::Ordering,
    collections::HashMap,
    io::{self, Read},
};

const BASE_MINS: i32 = 100;
const BASE_PRICE: i32 = 10;
const UNIT_MINS: f64 = 50.0;
const UNIT_PRICE: i32 = 3;

#[derive(Debug, Clone)]
struct StudentBill {
    name: String,
    price: i32,
}

fn main() -> io::Result<()> {
    let mut input = String::new();
    io::stdin().read_to_string(&mut input)?;

    let mut lines = input.trim().lines();

    let n: usize = lines.next().and_then(|l| l.parse().ok()).unwrap_or(0);

    let mut students: HashMap<String, i32> = HashMap::new();

    for _ in 0..n {
        if let Some(line) = lines.next() {
            let parts: Vec<&str> = line.split_whitespace().collect();

            if parts.len() != 2 {
                continue;
            }

            let time = parts[0];
            let name = parts[1];

            let time_parts: Vec<&str> = time.split(":").collect();
            if time_parts.len() != 2 {
                continue;
            }

            let hour = time_parts[0].parse::<i32>().unwrap_or(0);
            let mins = time_parts[1].parse::<i32>().unwrap_or(0);

            let total_mins = hour * 60 + mins;

            *students.entry(name.to_string()).or_insert(0) += total_mins;
        }
    }

    let mut bills: Vec<StudentBill> = Vec::new();

    for (name, total_mins) in students {
        let mut final_price = BASE_PRICE;
        let excess_mins = total_mins - BASE_MINS;

        if excess_mins > 0 {
            let excess_mins_f64 = excess_mins as f64;

            let unit_count = (excess_mins_f64 / UNIT_MINS).ceil() as i32;

            let excess_price = unit_count * UNIT_PRICE;
            final_price += excess_price
        }

        bills.push(StudentBill {
            name,
            price: final_price,
        });
    }

    bills.sort_by(|a, b| {
        let price_cmp = b.price.cmp(&a.price);

        if price_cmp != Ordering::Equal {
            return price_cmp;
        }

        a.name.cmp(&b.name)
    });

    for bill in bills {
        println!("{} {}", bill.name, bill.price);
    }

    Ok(())
}

Node(JavaScript)

const fs = require("fs");
const input = fs.readFileSync(0).toString().trim().split("\n");

const N = input[0];

const students = {};

for (let i = 1; i <= N; i++) {
  const [time, name] = input[i].trim().split(" ");
  const [hour, mins] = time.split(":");
  const tmp_total_mins = parseInt(hour) * 60 + parseInt(mins);

  students[name] = (students[name] || 0) + tmp_total_mins;
}

// 기본 제공 시간   :  100 분
// 기본 요금        :  10
// 기본  단위 시간  :  50
// 단위 요금        :  3

// [name, mins]
let bill = [];

for (const student of Object.entries(students)) {
  // 기본 제공 시간 100분 : 10원
  let tmp = student[1] - 100;
  if (tmp <= 0) {
    bill.push([student[0], 10]);
  } else {
    bill.push([student[0], 10 + Math.ceil(tmp / 50) * 3]);
  }
}

bill.sort((a, b) => {
  if (a[1] !== b[1]) {
    return b[1] - a[1];
  }
  return a[0].localeCompare(b[0]);
});

for (const [name, price] of bill) {
  console.log(`${name} ${price}`);
}

후기

코드 길이 차이 무엇

문자열 파싱부터, 해시맵 + 정렬까지 아주 다양한 부분을 연습하기 좋은 기본기 문제 아닐까.

흐어어억

하지만

화이팅화이팅

-뱅-