울타리 잘라내기(난이도 - 중)

2022. 9. 8. 10:59·알고리즘 (with JAVA)/분할 정복 알고리즘

1. 문제 설명

(1) 너비가 같은 N개의 나무 판자를 붙여 세운 울타리가 있습니다.

 

(2) 시간이 지남에 따라 판자들이 망가져 높이가 다 달라져서 기존의 울타리 일부를 직사각형으로

잘라내 재활용하고 싶습니다.

 

(3) 이때, 울타리를 구성하는 각 판자의 높이가 주어질 때, 잘라낼 수 있는 직사각형의 최대 크기를

계산하는 프로그램을 작성하세요.

 

( (b)는 (a)의 울타리에서 잘라낼 수 있는 많은 직사각형 중 가장 넓은 직사각형을 보여줍니다. )

( 단, (c) 처럼 비스듬히 잘래날 수 없습니다. )

 

 

 

 

2. 입출력 조건 및 예제

입력 조건

(1) 판자의 수 N(1<=N<=20000)이어야 한다.

(2) 높이는 모두 10,000 이하의 자연수입니다.

 

출력 조건

(1) 각 테스트 케이스당 정수 하나를 한 줄에 출력합니다.

 

 

입력 예제

3

7

7 1 5 9 6 7 3

7

1 4 4 4 4 1 1

4

1 8 2 2

 

출력 예제

20

16

8

 

 

 

 

3. 제약 조건

(1) 프로그램은 1초 안에 실행되어야 하며, 64MB 이하의 메모리를 사용해야 합니다.

 

 

 

 

4. 가정법

X

 

 

 

 

5. 기저 사례

(1) 판자가 하나밖에 없는 경우

( 즉, left == right )

 

 

 

 

6. 코드

: 두 코드가 존재한다.

 

(1)의 코드는 무식하게 접근하여 해결하는 방법을 보여준다. 그냥 단순하게 l번 판자에서 r번 판자까지 잘라내서

사각형을 만드는 넓이를 계산하는 이중 for문을 작성하는 것이다. 그러나, 입력의 최대 크기는 20,000번이므로

시간복잡도 O(n^2)으로는 힘들어 보입니다. 

( (1)의 코드는 아래의 코드를 이용한 것입니다. )

 

그러므로, 우리는 (2)의 코드처럼 분할 정복 알고리즘을 설계해야 합니다.

 

 

설계는 먼저 양쪽을 분할하는 부분 문제 2개와 양쪽 부분을 걸치는 부분 문제 1개로 아래처럼 나눠서 진행합니다.

( (1) 가장 큰 직사각형을 왼쪽 부분 문제에서만 잘라낼 수 있다. )

( (2) 가장 큰 직사각형을 오른쪽 부분 문제에서만 잘라낼 수 있다. )

( (3) 가장 큰 직사각형은 왼쪽 부분 문제와 오른쪽 부분 문제에 걸쳐 있다. )

 

 

이때, (3)에서는 경계선 기준으로 양쪽의 판자를 하나씩 포함하며 확장한다.

( 단, 왼쪽을 먼저 확장할 것인지 오른쪽을 먼저 확장할 것인지는 판자 크기순으로 정한다. )

 

 

(1) Brute-Force

public class Fence1 {
    private static BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
    private static StringTokenizer st ;
    private static StringBuilder builder = new StringBuilder();

    public static void main(String[] args) throws IOException {
        int c = Integer.parseInt(sc.readLine());

        for(int i=0; i<c; i++) {
            int N = Integer.parseInt(sc.readLine());			
            int[] h = new int[N];
            st = new StringTokenizer(sc.readLine());

            for(int j=0; j<N; j++) {
                h[j] = Integer.parseInt(st.nextToken());
            }
            builder.append(bruteForce(h) + "\n");
        }
        System.out.println(builder);
    }
    // 무식하게 접근하는 방식
    // (b-a+1) * (min(h[i]) 이때, h[i]는 가장 작은 원소
    private static int bruteForce(int[] h) {
        int ret=0;
        int N = h.length;

        for(int left=0; left<N; left++) {
            int minH = h[left];

            for(int right=left; right<N; right++) {
                minH = Math.min(minH, h[right]);
                ret = Math.max(ret, (right-left+1)*minH);
            }
        }
        return ret;
    }
}

 

 

 

(2) 분할 정복 

public class Fence2 {
    private static BufferedReader sc = new BufferedReader(new InputStreamReader(System.in));
    private static StringTokenizer st ;
    private static StringBuilder builder = new StringBuilder();
    private static int[] h;

    public static void main(String[] args) throws NumberFormatException, IOException {
        int c = Integer.parseInt(sc.readLine());

        for(int i=0; i<c; i++) {
            int N = Integer.parseInt(sc.readLine());
            h = null;
            h = new int[N];
            st = new StringTokenizer(sc.readLine());

            for(int j=0; j<N; j++) {
                h[j] = Integer.parseInt(st.nextToken());
            }
            builder.append(solve(0, N-1) + "\n");
        }
        System.out.println(builder);
    }

    private static int solve(int left, int right) {
        // 기저사례: 판자가 하나일 경우 그대로 값 리턴
        if(left == right) return h[left];

        // 주어진 원소를 중간으로 나누고
        // 초기에 중간을 기준으로 왼쪽과 오른쪽으로 계속 분할
        int mid = (left+right) / 2;
        int ret = Math.max(solve(left, mid), solve(mid+1,right));

        // 중간을 기준으로 양쪽에 걸쳐서 존재하는
        // 직사각형을 위해 분할
        int lo=mid, hi=mid+1;
        int height = Math.min(h[lo], h[hi]);

        // height의 값은 양쪽에서 걸쳐서 존재하기에
        // 직사각형을 그리기 때문에 *2 필요
        ret = Math.max(ret, height*2);

        // 먼저, lo와 hi가 left, right 부분으로 옮길 수 없다면, 종료
        while(lo<left || hi<right) {

            // h[lo-1]<h[hi+1]이라면, hi를 옮김
            // 아니라면 lo를 옮김
            if(hi<right && (lo==left || h[lo-1] < h[hi+1])) {;
                height = Math.min(height, h[++hi]);
            }else {
                height = Math.min(height, h[--lo]);
            }

            // 최종적으로 값 계산
            ret = Math.max(ret, height * (hi-lo+1));
        }
        return ret;
    }
}

 

 

 

 

7. 시간 복잡도

- 문제를 항상 절반으로 나누어서 재귀 호출(O(lgn))하고, 후처리 계산을 하는 반복문(O(n))이 있다.

 

- 이 시간 복잡도는 병합 정렬과 같은 시간 복잡도 O(nlgn)을 가지게 된다.

( n=20,000 -> nlgn은 290,000 이기 떄문에 아주 쉽게 1초 안에 풀 수 있다. )

 

저작자표시 (새창열림)

'알고리즘 (with JAVA) > 분할 정복 알고리즘' 카테고리의 다른 글

팬미팅 (난이도 - 상)  (0) 2022.09.14
카라츠바의 빠른 곱셈 알고리즘(2) - O(N^lg3)  (1) 2022.09.14
카라츠바의 빠른 곱셈 알고리즘(1) - O(N^2)  (0) 2022.09.13
쿼드 트리 뒤집기(난이도 - 하)  (0) 2022.09.06
행렬의 거듭 제곱 구하기(난이도: 하)  (0) 2022.09.02
'알고리즘 (with JAVA)/분할 정복 알고리즘' 카테고리의 다른 글
  • 카라츠바의 빠른 곱셈 알고리즘(2) - O(N^lg3)
  • 카라츠바의 빠른 곱셈 알고리즘(1) - O(N^2)
  • 쿼드 트리 뒤집기(난이도 - 하)
  • 행렬의 거듭 제곱 구하기(난이도: 하)
백_곰
백_곰
  • 백_곰
    친절한 코딩
    백_곰
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 알고리즘 (with JAVA)
        • 기본 알고리즘
        • 완전 탐색
        • 분할 정복 알고리즘
        • 동적 계획법
        • 탐욕법
        • 코딩 테스트 기출 문제
        • 코드트리 조별과제
      • 백준 (with JAVA)
        • 완전 탐색
        • 분할 정복
        • 그 외
      • 자바
        • 개발 환경 구축하기
        • 팁
        • 기본적인 개념
        • 컬렉션 프레임워크
        • 프로세스와 쓰레드
        • 지네릭스
        • 람다식
        • 스트림
        • 입출력 IO
        • 네트워킹
        • 열거형(enums)
        • java.lang 패키지
        • java.time 패키지
        • 유용한 클래스들
        • 형식화 클래스들
      • 안드로이드 with 자바
        • 응용 문제들
        • 자잘한 문제들
        • 오류 보고서
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      다형성
      불안정 정렬
      InputStream
      java.time 패키지
      snail
      Arrays
      serializable
      outputstream
      소켓 프로그래밍
      알고스팟
      안드로이드 스튜디오
      스트림
      문자 기반 스트림
      람다식
      file
      제자리 정렬
      코딩테스트
      선택 정렬
      역직렬화
      중간연산
      코딩트리조별과제
      map()
      안정 정렬
      Collections Framework
      java.lang패키지
      유용한 클래스
      ServerSocket
      TCP 소켓 프로그래밍
      코드트리
      자바 개념
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    백_곰
    울타리 잘라내기(난이도 - 중)
    상단으로

    티스토리툴바