TIL/SW&백준 문제 리뷰

[백준] 1931 회의실 배정

JoJobum 2021. 8. 9.

1931번: 회의실 배정 (acmicpc.net)

 

1931번: 회의실 배정

(1,4), (5,7), (8,11), (12,14) 를 이용할 수 있다.

www.acmicpc.net

문제

한 개의 회의실이 있는데 이를 사용하고자 하는 N개의 회의에 대하여 회의실 사용표를 만들려고 한다. 각 회의 I에 대해 시작시간과 끝나는 시간이 주어져 있고, 각 회의가 겹치지 않게 하면서 회의실을 사용할 수 있는 회의의 최대 개수를 찾아보자. 단, 회의는 한번 시작하면 중간에 중단될 수 없으며 한 회의가 끝나는 것과 동시에 다음 회의가 시작될 수 있다. 회의의 시작시간과 끝나는 시간이 같을 수도 있다. 이 경우에는 시작하자마자 끝나는 것으로 생각하면 된다.

 

 

내풀이

회의실 배정은 알고리즘 수업을 들을 때 예제로 나올 정도로 그리디 알고리즘의 대표적인 문제

그리디 알고리즘을 알기 전에 0/1 knapsack과 비슷하게 가성비(?) 등의 그럴듯한 생각의 함정에 빠지기 쉬웠다.

끝나는 시간 순으로 정렬하고 순서대로 되는대로 회의실을 배정하면 최대한 많이 집을 수 있음

왜냐하면 가장 적은 시간 내에 회의실 하나를 배정하는 것이 최적의 해결인데 

위의 예시를 봤을 때 10 이전에 최대한 빨리 회의실을 하나 배정하는 방법은? => 최대한 회의 종료시간이 빠른 회의를 선택하는 것이다. 즉 회의 시간을 최소화하는 것이 아닌 전체 시간(회의를 위해 기다리는 중간 비는시간 포함)을 최소화하는 기준으로 생각하는 것이다.

이걸 하나의 subProblem으로 (1,4)를 배정하고, 4 이후의 범위에서 subProblem을 해결하면 (5,7) 을 선택하게 될 것이다. 이렇게 subProblem 들을 최적의 방법으로 해결하여 합치면 처음에 구하려고 했던 답에 도달하게 된다.

 

위의 방법을 구현한 방법은 

우선 회의 시간이 끝나는 시간이 빠른순, 같다면 시작시간이 더 빠른순으로 정렬한 후, Greedy 하게 되는대로 선택하며 선택한 회의의 숫자를 세었다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.Arrays;
import java.util.StringTokenizer;

public class Main{
	static BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
	static StringBuilder output = new StringBuilder();
	static String src="11\r\n"
			+ "1 4\r\n"
			+ "3 5\r\n"
			+ "0 6\r\n"
			+ "5 7\r\n"
			+ "3 8\r\n"
			+ "5 9\r\n"
			+ "6 10\r\n"
			+ "8 11\r\n"
			+ "12 12\r\n"
			+ "8 12\r\n"
			+ "12 14";
	
	static int n;
	static int result=0;
	
	public static void main(String arg[]) throws IOException {
		//input= new BufferedReader(new InputStreamReader(System.in));
		input= new BufferedReader(new StringReader(src));
		
		n=Integer.parseInt(input.readLine());
		StringTokenizer str;
		
		int[][] data=new int[n][2];
		
		for(int i=0;i<n;i++) {
			str=new StringTokenizer(input.readLine());
			data[i][0]=Integer.parseInt(str.nextToken());
			data[i][1]=Integer.parseInt(str.nextToken());
		}
		
		quickSort(data);
		
//		for(int i=0;i<n;i++) {
//			System.out.println(Arrays.toString(data[i]));
//		}
		
		
		find(data,0,0);
		System.out.print(result);
		
	}
	//회의실 선택하는 메소드, 끝나는 시간 오름차순으로 정렬되있기에 greedy하게 선택함
	public static void find(int[][] data,int start,int index) {
		if(index>=n) { //범위 벗어나면 리턴으로 끝내줌
			return;
		}
		if(start<=data[index][0]) { //회의 시작 시간이 허락만해주면 바로 집음 
			start=data[index][1];
			result++;
		}
		find(data,start,index+1);
	}
	
	//퀵소트 메소드 구현 부분
	public static void quickSort(int[][] arr) {
        sort(arr, 0, arr.length - 1);
    }

    public static void sort(int[][] arr, int low, int high) {
        if (low >= high) return;

        int mid = partition(arr, low, high);
        sort(arr, low, mid - 1);
        sort(arr, mid, high);
    }

    public static int partition(int[][] arr, int low, int high) {
        int pivot = arr[(low + high) / 2][1];
        while (low <= high) {
            while (arr[low][1] < pivot) 
            	low++;
            while (arr[high][1] > pivot) 
            	high--;
            if (low <= high) {
                swap(arr, low, high);
                low++;
                high--;
            }
        }
        return low;
    }
    //스왑부분을 조정함으로서 원하는대로 (회의 시간이 끝나는 시간이 빠른순, 같다면 시작시간이 더 빠른순) 정렬
    public static void swap(int[][] arr, int i, int j) {
        int tmp = arr[i][1];
        arr[i][1] = arr[j][1];
        arr[j][1] = tmp;
        
      //끝부분 숫자가 같은데 이미 시작부분 숫자도 정렬되있는 경우에는 스왑안함
        if(arr[i][1] == arr[j][1]&&arr[i][0] < arr[j][0]) { 
        }else {
        	tmp = arr[i][0];
            arr[i][0] = arr[j][0];
            arr[j][0] = tmp;
        }
        
    }
	
	
}
반응형

'TIL > SW&백준 문제 리뷰' 카테고리의 다른 글

[백준] 2504 괄호의 값  (0) 2021.08.19
[백준] 11000 강의실  (0) 2021.08.18
[백준] 2309 일곱 난쟁이  (0) 2021.08.05
[백준] 11727 2*N 타일링 2  (0) 2021.07.31
[백준] 11053 가장 긴 증가하는 부분 수열  (0) 2021.07.29

댓글