4. 셔틀버스(난이도: 중)

카카오에서는 무료 셔틀버스를 운행하기 때문에 판교역에서 편하게 사무실로 올 수 있다. 카카오의 직원은 서로를 ‘크루’라고 부르는데, 아침마다 많은 크루들이 이 셔틀을 이용하여 출근한다.

이 문제에서는 편의를 위해 셔틀은 다음과 같은 규칙으로 운행한다고 가정하자.

  • 셔틀은 09:00부터 총 nt분 간격으로 역에 도착하며, 하나의 셔틀에는 최대 m명의 승객이 탈 수 있다.
  • 셔틀은 도착했을 때 도착한 순간에 대기열에 선 크루까지 포함해서 대기 순서대로 태우고 바로 출발한다. 예를 들어 09:00에 도착한 셔틀은 자리가 있다면 09:00에 줄을 선 크루도 탈 수 있다.

일찍 나와서 셔틀을 기다리는 것이 귀찮았던 콘은, 일주일간의 집요한 관찰 끝에 어떤 크루가 몇 시에 셔틀 대기열에 도착하는지 알아냈다. 콘이 셔틀을 타고 사무실로 갈 수 있는 도착 시각 중 제일 늦은 시각을 구하여라.

단, 콘은 게으르기 때문에 같은 시각에 도착한 크루 중 대기열에서 제일 뒤에 선다. 또한, 모든 크루는 잠을 자야 하므로 23:59에 집에 돌아간다. 따라서 어떤 크루도 다음날 셔틀을 타는 일은 없다.

입력 형식

셔틀 운행 횟수 n, 셔틀 운행 간격 t, 한 셔틀에 탈 수 있는 최대 크루 수 m, 크루가 대기열에 도착하는 시각을 모은 배열 timetable이 입력으로 주어진다.

  • 0 < n ≦ 10
  • 0 < t ≦ 60
  • 0 < m ≦ 45
  • timetable은 최소 길이 1이고 최대 길이 2000인 배열로, 하루 동안 크루가 대기열에 도착하는 시각이 HH:MM 형식으로 이루어져 있다.
  • 크루의 도착 시각 HH:MM00:01에서 23:59 사이이다.

출력 형식

콘이 무사히 셔틀을 타고 사무실로 갈 수 있는 제일 늦은 도착 시각을 출력한다. 도착 시각은 HH:MM 형식이며, 00:00에서 23:59 사이의 값이 될 수 있다.

입출력 예제

n t m timetable answer
1 1 5 [“08:00”, “08:01”, “08:02”, “08:03”] “09:00”
2 10 2 [“09:10”, “09:09”, “08:00”] “09:09”
2 1 2 [“09:00”, “09:00”, “09:00”, “09:00”] “08:59”
1 1 5 [“00:01”, “00:01”, “00:01”, “00:01”, “00:01”] “00:00”
1 1 1 [“23:59”] “09:00”
10 60 45 [“23:59”,”23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”, “23:59”] “18:00”

문제 해설

쉬워 보이는데 어려운 문제가 바로 이 문제였던 거 같네요. 당초 난이도를 ‘중’으로 두고 문제를 중간 즈음에 배치하였는데, 시간을 계산하는 부분에서 많은 분들이 어려워하셨던 거 같습니다.

예를 들어 2번 입출력 예제의 경우 ["09:10", "09:09", "08:00"]인데 이 경우 두 번째 버스는 9:10분에 출발하기 때문에 9:10분에 오면 되지 않느냐 많이들 혼동하셨을 거 같아요. 하지만 9:00에 오는 버스는 8:00에 대기하는 크루 1명만 탑승할 수 있고, 따라서 9:10 버스에는 남아 있는 두 명이 모두 타게 됩니다. 따라서 좀 더 이른 9:09에 와야 탑승할 수 있습니다.

전체 계산은 어렵지 않지만 이처럼 정확하게 시간 계산을 해야 하는 부분이 많고 마지막 버스 시간까지 빈틈없이 계산해야 해서 많은 분들이 실수를 한 거 같습니다. 이 문제는 정답률이 두 번째로 낮은 26.79%입니다.





난이도 중 문제임에도 정답률이 낮다. 실제로 풀어보니 시간이 많이 걸렸다. 도중에 다른 문제로 교체한 사람들이 있어서 그런 듯 하다.


모든 크루들의 시간을 분으로 변경한다는 것은 처음부터 정해져 있던 내용이고 그 이후의 행동이 중요한데


vector나 list를 자동 정렬해주거나 removeif 메소드를 지원하는게 장점인 것 같다. C++은 포기하고 자바만 파두는게 나을지도 모르겠다.


콘은 무조건 막차만 타야 하기 때문에 앞 차량에 태울 수 있는 경우 모두 태워서 보내야 한다.


막차 시간 이후에 오는 사람은 고려할 필요가 없기 때문에 모두 보내버리면 된다.


그 후엔 막차 버스를 기준으로 허용량을 넘는가 아닌가만 고려하면 시간은 걸리지만 어려운 문제는 아니었다.



package kakao02;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
import java.util.Vector;
import java.util.List;

public class kakao02 {

	public static void main(String[] args) {
		String a = Solution(1, 1, 5, new String[] { "08:00", "08:01", "08:02", "08:03" });
		String b = Solution(2, 10, 2, new String[] { "09:10", "09:09", "08:00" });
		String c = Solution(2, 1, 2, new String[] { "09:00", "09:00", "09:00", "09:00" });
		String d = Solution(1, 1, 5, new String[] { "00:01", "00:01", "00:01", "00:01", "00:01" });
		String e = Solution(1, 1, 1, new String[] { "23:59" });
		String f = Solution(10, 60, 45, new String[] { "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59",
				"23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59", "23:59" });
		System.out.println(a);
		System.out.println(b);
		System.out.println(c);
		System.out.println(d);
		System.out.println(e);
		System.out.println(f);
	}

	public static String Solution(int n, int t, int m, String[] timetable) {

		Vector<Integer> time = new Vector<Integer>();
		for (int i = 0; i < timetable.length; ++i) {// 크루들의 도착 시간을 int 형태로 Vector에 저장
			String[] temp = timetable[i].split(":");
			time.add(Integer.parseInt(temp[0]) * 60 + Integer.parseInt(temp[1]));
		}
		Collections.sort(time);// 도착 시간을 오름차순으로 정렬

		Vector<Integer> busTime = new Vector<Integer>();// 버스의 도착 시간을 저장하기 위한 배열
		busTime.add(540);// 첫 시작은 540 == "09:00"
		for (int i = 1; i < n; ++i) {// 운행 횟수만큼 추가된 시간이 더해진 운행시간 값을 저장.
			busTime.add(540 + t * i);
		}
		int lastBusTime = busTime.get(busTime.size() - 1);// 버스 막차 시간 확인

		time.removeIf(i -> i > lastBusTime);// 막차시간 이후에 오는 크루는 고려할 필요 없음. 예제6

		if (n > 1) {
			for (int i = 0; i < busTime.size() - 1; ++i) {// 콘은 막차를 타야 한다. 그 이전에 태울 수 있는 사람은 모두 삭제.
				for (int j = 0; j < m; ++j) {
					if (time.size() != 0)
						if (busTime.get(i) >= time.get(0)) {
							time.remove(0);
						}
				}
			}
		}

		if (m <= time.size())// 남은 인원수가 막차 허용량보다 많은 경우
		{
			for (int i = 0; i < m; ++i) {
				if (lastBusTime >= time.get(0)) {
					if (i < m - 1) {// 막차 허용량 안에 들어갈 경우 앞 사람부터 삭제
						time.remove(0);
					} else {//크루가 버스에 탈 수 있는데 마지막 허용량일 경우
						int lastBusHour = time.get(0) / 60;//해당 크루의 시간을 받아오고
						
						int lastBusMin = (time.get(0) % 60);//해당 크루의 분을 받아온 후에, -1분 해준다.
						if (lastBusMin == 0) {
							lastBusMin = 59;
							lastBusHour -= 1;
						} else
							lastBusMin--;
						
						String timeText = String.format("%02d", lastBusHour);//String이 아니라 int를 입력해야 한다.
						timeText += ":";
						timeText += String.format("%02d", lastBusMin);
						return timeText;
					}
				} else if (i < m) {//허용량이 남았지만 더이상 크루들이 타지 못하는 경우. 위에서 removeif를 했기에 쓸 일은 없다.
					//마지막 버스 시간에 도착하면 된다.
					int lastBusHour = lastBusTime / 60;
					int lastBusMin = lastBusTime % 60;
					String timeText = String.format("%02d", lastBusHour);
					timeText += ":";
					timeText += String.format("%02d", lastBusMin);
					return timeText;
				}
			}
		} else// 남은 인원수가 막차 허용량보다 적은 경우. 마지막 버스 도착 시간에 맞춰서 타면 된다.
		{
			int lastBusHour = lastBusTime / 60;
			int lastBusMin = lastBusTime % 60;
			String timeText = String.format("%02d", lastBusHour);
			timeText += ":";
			timeText += String.format("%02d", lastBusMin);
			return timeText;
		}
		return null;

	}
}

3. 캐시(난이도: 하)




지도개발팀에서 근무하는 제이지는 지도에서 도시 이름을 검색하면 해당 도시와 관련된 맛집 게시물들을 데이터베이스에서 읽어 보여주는 서비스를 개발하고 있다.
이 프로그램의 테스팅 업무를 담당하고 있는 어피치는 서비스를 오픈하기 전 각 로직에 대한 성능 측정을 수행하였는데, 제이지가 작성한 부분 중 데이터베이스에서 게시물을 가져오는 부분의 실행시간이 너무 오래 걸린다는 것을 알게 되었다.
어피치는 제이지에게 해당 로직을 개선하라고 닦달하기 시작하였고, 제이지는 DB 캐시를 적용하여 성능 개선을 시도하고 있지만 캐시 크기를 얼마로 해야 효율적인지 몰라 난감한 상황이다.

어피치에게 시달리는 제이지를 도와, DB 캐시를 적용할 때 캐시 크기에 따른 실행시간 측정 프로그램을 작성하시오.

입력 형식

  • 캐시 크기(cacheSize)와 도시이름 배열(cities)을 입력받는다.
  • cacheSize는 정수이며, 범위는 0 ≦ cacheSize ≦ 30 이다.
  • cities는 도시 이름으로 이뤄진 문자열 배열로, 최대 도시 수는 100,000개이다.
  • 각 도시 이름은 공백, 숫자, 특수문자 등이 없는 영문자로 구성되며, 대소문자 구분을 하지 않는다. 도시 이름은 최대 20자로 이루어져 있다.

출력 형식

  • 입력된 도시이름 배열을 순서대로 처리할 때, “총 실행시간”을 출력한다.

조건

  • 캐시 교체 알고리즘은 LRU(Least Recently Used)를 사용한다.
  • cache hit일 경우 실행시간은 1이다.
  • cache miss일 경우 실행시간은 5이다.

입출력 예제

캐시크기(cacheSize) 도시이름(cities) 실행시간
3 [“Jeju”, “Pangyo”, “Seoul”, “NewYork”, “LA”, “Jeju”, “Pangyo”, “Seoul”, “NewYork”, “LA”] 50
3 [“Jeju”, “Pangyo”, “Seoul”, “Jeju”, “Pangyo”, “Seoul”, “Jeju”, “Pangyo”, “Seoul”] 21
2 [“Jeju”, “Pangyo”, “Seoul”, “NewYork”, “LA”, “SanFrancisco”, “Seoul”, “Rome”, “Paris”, “Jeju”, “NewYork”, “Rome”] 60
5 [“Jeju”, “Pangyo”, “Seoul”, “NewYork”, “LA”, “SanFrancisco”, “Seoul”, “Rome”, “Paris”, “Jeju”, “NewYork”, “Rome”] 52
2 [“Jeju”, “Pangyo”, “NewYork”, “newyork”] 16
0 [“Jeju”, “Pangyo”, “Seoul”, “NewYork”, “LA”] 25



LRU가 뭔지 대충 검색해봤고, 거기서 명시한 조건만 만족하면 되는건가 해서 풀어보았다. 답은 맞는데 LRU가 제대로 된건지 흠..

처음에 임의의 배열을 받는다고 할때 컴파일러에서 배열을 어떻게 입력을 하길래 크기를 인지하고 입력을 받아야 하지? 하고 생각을 했었는데

카카오 모의 테스트에 들어가보니 별도의 solution 함수를 수정할 수 있게 되어져 있고 해당 함수에 배열이 인자값으로 들어오는 형태였다.

이걸 몰라서 몇 시간을 고생했었는지..


package kakao02;

import java.util.Scanner;
public class kakao02 {

	public static void main(String[] args)
	{
		String[] arr = new String[]{"Jeju", "Pangyo", "Seoul","NewYork", "LA", "Jeju","Pangyo", "Seoul", "NewYork", "LA"};
		String[] arr2 = new String[] {"Jeju", "Pangyo", "Seoul", "NewYork", "LA"};
		System.out.println(Solution(arr2));
	}
	public static int Solution(String[] cities)
	{
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		//cacheSize = new int[t];
		int time = 0;
		int bufferCount = 0;
		String[] cacheSize = new String[n];
		int[] cacheNum = new int[n];
		
		for(int i = 0; i<cities.length;++i)
		{
			boolean check = false;
			for(int j = 0; j<n;++j)
			{
				if(cities[i].toUpperCase().equals(cacheSize[j]))
				{
					time++;
					check = true;
					cacheNum[j] = i;
					break;
				}
			}
			if(!check) {			
				if(bufferCount < n) {
					cacheSize[bufferCount] = cities[i].toUpperCase();
					cacheNum[bufferCount] = i;
					bufferCount++;
				}
				else
				{
					int lastNum = 1000000;
					int count = -1;
					for(int j = 0; j < n;++j)
					{
						if(lastNum > cacheNum[j])
						{
							lastNum = cacheNum[j];
							count = j;
						}
					}
					if(n != 0)
					{
					cacheNum[count] = i;
					cacheSize[count] = cities[i].toUpperCase();
					}
				}
				time+=5;
			}
		}
		
		
		return time;
	}
}


+ Recent posts