[백준] 2343번: 기타 레슨

조소복·2023년 1월 16일
0

문제

강토는 자신의 기타 강의 동영상을 블루레이로 만들어 판매하려고 한다. 블루레이에는 총 N개의 강의가 들어가는데, 블루레이를 녹화할 때, 강의의 순서가 바뀌면 안 된다. 순서가 뒤바뀌는 경우에는 강의의 흐름이 끊겨, 학생들이 대혼란에 빠질 수 있기 때문이다. 즉, i번 강의와 j번 강의를 같은 블루레이에 녹화하려면 i와 j 사이의 모든 강의도 같은 블루레이에 녹화해야 한다.

강토는 이 블루레이가 얼마나 팔릴지 아직 알 수 없기 때문에, 블루레이의 개수를 가급적 줄이려고 한다. 오랜 고민 끝에 강토는 M개의 블루레이에 모든 기타 강의 동영상을 녹화하기로 했다. 이때, 블루레이의 크기(녹화 가능한 길이)를 최소로 하려고 한다. 단, M개의 블루레이는 모두 같은 크기이어야 한다.

강토의 각 강의의 길이가 분 단위(자연수)로 주어진다. 이때, 가능한 블루레이의 크기 중 최소를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 강의의 수 N (1 ≤ N ≤ 100,000)과 M (1 ≤ M ≤ N)이 주어진다. 다음 줄에는 강토의 기타 강의의 길이가 강의 순서대로 분 단위로(자연수)로 주어진다. 각 강의의 길이는 10,000분을 넘지 않는다.

출력

첫째 줄에 가능한 블루레이 크기중 최소를 출력한다.

예제 입력 1

9 3
1 2 3 4 5 6 7 8 9

예제 출력 1

17

힌트

강의는 총 9개이고, 블루레이는 총 3개 가지고 있다.

1번 블루레이에 1, 2, 3, 4, 5, 2번 블루레이에 6, 7, 3번 블루레이에 8, 9 를 넣으면 각 블루레이의 크기는 15, 13, 17이 된다. 블루레이의 크기는 모두 같아야 하기 때문에, 블루레이의 크기는 17이 된다. 17보다 더 작은 크기를 가지는 블루레이를 만들 수 없다.

문제 풀이

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

public class BJ2343 {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st=new StringTokenizer(br.readLine());

        int N=Integer.parseInt(st.nextToken());
        int M=Integer.parseInt(st.nextToken());

        int[] course=new int[N];

        int right=0;
        st=new StringTokenizer(br.readLine());
        for(int i=0;i<N;i++){
            course[i]=Integer.parseInt(st.nextToken());
            right+=course[i];
        }

        int left=0;
        while(left<=right){
            int mid=(left+right)/2;

            int cnt=0;
            int sum=0;
            for(int i=0;i<N;i++){
                // 블루레이 크기 넘어가는 경우 mid 값 키워줘야 함
                if(course[i]>mid){
                    cnt=M+1;
                    break;
                }
                sum+=course[i];

                if(sum>mid){
                    cnt++;
                    sum=course[i];
                    if(i==N-1) cnt++;
                }
                else if(sum==mid){
                    cnt++;
                    sum=0;
                }
                else{
                    if(i==N-1) cnt++;
                }
            }

            // 가능한 블루레이 개수가 M보다 많다면 블루레이 길이를 늘린다.
            if(cnt>M){
                left=mid+1;
            }
            else{
                right=mid-1;
            }
        }

        System.out.println(left);
    }
}

고려할 점

  • 각 블루레이는 주어진 i와 j까지의 순서를 지켜야한다.
  • 블루레이의 길이는 각 강의의 크기보다 크거나 같아야한다.

순서를 지켜야한다는 것은 주어진 강의의 순서를 바꾸면 안된다는 뜻이다.

즉, 이분탐색을 한다고 정렬을 하는 경우가 많은데 정렬없이 주어진 값을 그대로 이용해야한다는 뜻이다.

또한 강의는 어색함 없이 모든 강의가 순서대로 이어져야하므로 하나의 강의 크기가 블루레이 크기보다 크면 안된다.

강의 크기가 더 크다면 블루레이에 담을 수 없기 때문에 강의를 순서대로 담을 수 없다.

두 가지 점을 고려하면 문제를 풀 수 있다.

이분탐색

int left=0;
while(left<=right){
    int mid=(left+right)/2;

    int cnt=0;
    int sum=0;
    for(int i=0;i<N;i++){
        // 블루레이 크기 넘어가는 경우 mid 값 키워줘야 함
        if(course[i]>mid){
            cnt=M+1;
            break;
        }
        sum+=course[i];

        if(sum>mid){
            cnt++;
            sum=course[i];
            if(i==N-1) cnt++;
        }
        else if(sum==mid){
            cnt++;
            sum=0;
        }
        else{
            if(i==N-1) cnt++;
        }
    }

    // 가능한 블루레이 개수가 M보다 많다면 블루레이 길이를 늘린다.
    if(cnt>M){
        left=mid+1;
    }
    else{
        right=mid-1;
    }
}

각 강의의 순서대로 길이를 차례대로 더하여 mid 값을 넘기지 않는 묶음을 나누어 cnt 개수를 구한다.

cnt 값이 M보다 크다면 블루레이의 크기를 키울 필요가 있고 작거나 같다면 블루레이의 최소값을 구해야하기 때문에 크기를 줄인다.

cnt 값 구하기

int cnt=0;
int sum=0;
for(int i=0;i<N;i++){
    // 블루레이 크기 넘어가는 경우 mid 값 키워줘야 함
    if(course[i]>mid){
        cnt=M+1;
        break;
    }
    sum+=course[i];

    if(sum>mid){
        cnt++;
        sum=course[i];
        if(i==N-1) cnt++;
    }
    else if(sum==mid){
        cnt++;
        sum=0;
    }
    else{
        if(i==N-1) cnt++;
    }
}

강의의 크기를 하나씩 더해주는데 고려해야하는 점에서 설명했듯이 하나의 강의 크기가 블루레이의 크기보다 커지면 순서대로 강의를 담을 수 없기 때문에 그 경우에는 for문을 탈출하고 블루레이의 크기를 키워줘야한다.

아니라면 sum에 강의의 길이를 하나씩 더해가면서 묶음을 구해본다.

블루레이의 크기보다 sum이 커지게 되면 cnt 값을 증가시켜주는데 이때 mid 값보다 커졌다는 뜻은 i번째 강의는 해당 블루레이에 담을 수 없으므로 sum=course[i] 작업을 해준다.

또 주의해야할 점은 마지막 강의만 따로 블루레이에 묶이게 되는 경우이다.

즉, i번째 강의의 크기를 더했을 때 mid 값을 넘어간다면 cnt 값은 마지막 블루레이의 개수도 더해주어야하기 때문에

if(i==N-1) cnt++;

작업을 해준다.

그리고 sum 값이 블루레이의 크기와 똑같다면 이전의 경우와 다르게 i번째 강의까지 함께 묶일 수 있기 때문에 sum=0을 해준다.

sum값이 블루레이 크기보다 작은데 마지막 강의라면 cnt 값을 증가해준다.


이 과정을 반복하여 이분탐색을 진행하면 블루레이의 최소값을 구할 수 있다.


처음에 이분탐색은 당연히 정렬을 해야한다는 생각때문에 아무생각없이 course 배열을 정렬을 하고 시작했더니 답을 도출할 수 없었다.

문제를 다시 읽어보니 강의는 순서대로 진행되어야한다는 지문을 읽고 정렬을 하는 코드를 삭제했고

left값을 0부터 시작했기 때문에 for문 안에서 블루레이의 크기를 넘어가는 강의가 있다면 if문으로 조건을 처리해줘야했다.

이분탐색이라고 정해진 코드를 치려고 했다가 시간이 더 많이 걸렸던 문제였다.

문제를 좀 더 잘 읽어보고 생각을 하면서 코드를 짤 수 있어야할 것 같다.

profile
개발을 꾸준히 해보자

0개의 댓글