본문 바로가기

Problem Solving/프로그래머스

[프로그래머스/파이썬] 최소직사각형 해설 및 증명

문제 


문제 설명

명함 지갑을 만드는 회사에서 지갑의 크기를 정하려고 합니다. 다양한 모양과 크기의 명함들을 모두 수납할 수 있으면서, 작아서 들고 다니기 편한 지갑을 만들어야 합니다. 이러한 요건을 만족하는 지갑을 만들기 위해 디자인팀은 모든 명함의 가로 길이와 세로 길이를 조사했습니다.

아래 표는 4가지 명함의 가로 길이와 세로 길이를 나타냅니다.

명함 번호 가로 길이 세로 길이
1 60 50
2 30 70
3 60 30
4 80 40

가장 긴 가로 길이와 세로 길이가 각각 80, 70이기 때문에 80(가로) x 70(세로) 크기의 지갑을 만들면 모든 명함들을 수납할 수 있습니다. 하지만 2번 명함을 가로로 눕혀 수납한다면 80(가로) x 50(세로) 크기의 지갑으로 모든 명함들을 수납할 수 있습니다. 이때의 지갑 크기는 4000(=80 x 50)입니다.

모든 명함의 가로 길이와 세로 길이를 나타내는 2차원 배열 sizes가 매개변수로 주어집니다. 모든 명함을 수납할 수 있는 가장 작은 지갑을 만들 때, 지갑의 크기를 return 하도록 solution 함수를 완성해주세요.

 

제한사항

  • sizes의 길이는 1 이상 10,000 이하입니다.

 

입출력 예

sizes result
[[60, 50], [30, 70], [60, 30], [80, 40]] 4000
[[10, 7], [12, 3], [8, 15], [14, 7], [5, 15]] 120
[[14, 4], [19, 6], [6, 16], [18, 7], [7, 11]] 133

입출력 예 #1
문제 예시와 같습니다.

입출력 예 #2
명함들을 적절히 회전시켜 겹쳤을 때, 3번째 명함(가로: 8, 세로: 15)이 다른 모든 명함보다 크기가 큽니다. 따라서 지갑의 크기는 3번째 명함의 크기와 같으며, 120(=8 x 15)을 return 합니다.

입출력 #3
명함들을 적절히 회전시켜 겹쳤을 , 모든 명함을 포함하는 가장 작은 지갑의 크기는 133(=19 x 7)입니다.

 


문제 풀이


처음 문제를 봤을 땐 케이스에 따라 나눠가면서 풀어야한다고 생각하고 코드를 짜다가 많은 반례를 만났다.

분명 프로그래머스 Lv.1인데 DP문제인가..? 하는 고민도 하였다.

 

다시 생각해보니 직사각형의 넓이가 최소화되기 위해서는

양 변의 길이가 길어지는 것보다 한 변의 길이만 길어지는 것이 이득임을 기억했다.

 

우리가 두 변의 길이가 10이 되도록하는 직사각형의 넓이들은

1 x 9, 9 x 1 = 9

2 x 8, 8 x 2 = 16

...

5 x 5 = 25

 

양 변의 길이 합이 작아질 수록 넓이가 작아지는 것은 자명하고

두 변의 합의 길이가 고정됐을 때 두 변의 길이가 같다면 넓이가 가장 큰 것을 알 수 있는데 증명하자면

  1. x와 y가 가로 세로 변일 때, x + y = z 이며 z는 고정 값이라고 가정
  2. 두 변의 넓이는 x * y로 x * (z - x)와 같다. 풀어 쓰게되면 (zx - x2) 이며
  3. 이 함수는 위로 볼록한 형태를 가지고 있으므로 최대값을 가지고 미분하여 0되는 지점 (기울기가 0인지점)에서 최대값을 가진다.
  4. 미분하면 z - 2x이며 x = z/2 인 부분에서 최대값을 가진다. 즉 x,y가 값이 같을 수록 값이 커지고 차이가 클 수록 작은 값이 됨을 알 수 있다.

따라서 한 변은 최소값을 가지도록 하고 한 변은 최대값을 가지도록 하여 두 변의 차이가 크게 만들면 정답을 찾을 수 있다.


코   드


처음에 C++이 아직 익숙해 C++ 형식으로 코드를 짰다.

def solution(sizes):
    w, h = 0, 0

    for size in sizes:
        width, height = max(size[0], size[1]), min(size[0], size[1])
        w = max(width, w)
        h = max(height, h)

    return w * h

 

이후 다른 사람들의 풀이를 보며 정리한 코드는 아래와 같다. 결국 위 아래 코드 모두 같은 역할이다.

 

def solution(sizes):
    answer = max(max(x) for x in sizes) * max(min(x) for x in sizes)
    
    return answer

느낀점: 파이썬은 참 다양한 것을 모듈화해놨다.. 적응하기까지 시간이 걸릴 것 같다.

그리고 그리디는 참 증명이 어렵고 탐욕법인지 보고 아직까지 바로 알아채지 못하는게 아쉽다.

 

링크: https://school.programmers.co.kr/learn/courses/30/lessons/86491

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr