Front-End/Algorithm

[프로그래머스 / JavaScript] Lv.1 최소직사각형

Olivia Kim 2023. 2. 14. 10:07
반응형

 

문제

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

아래 표는 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의 원소는 [w, h] 형식입니다.
    • w는 명함의 가로길이를 나타냅니다.
    • h는 명함의 세로 길이를 나타냅니다.
    • w와 h는 1 이상 1,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)입니다.

 

 


 

내가 작성한 답안

#1

function solution(sizes) {
    let longer = [];
    let shorter = [];

    for(let i = 0; i < sizes.length; i++) {
        if(sizes[i][0] > sizes[i][1]) {
            longer.push(sizes[i][0]);
            shorter.push(sizes[i][1]);
        } else {
            longer.push(sizes[i][1]);
            shorter.push(sizes[i][0]);
        }
    }

    let biggerSideMax = Math.max(...longer);
    let smallerSideMax = Math.max(...shorter);
    
    return biggerSideMax * smallerSideMax;
}

맨 처음 풀었던 방법이다. 명함의 '긴 변'중에서 가장 큰 수, 명함의 '짧은 변'중에서 가장 큰 수를 찾아내 그 둘을 곱하여 최종 답을 찾아낼 생각이었다.

 

[가로, 세로] 형식으로 들어오는 입력값 중 명함의 긴 변, 짧은 변을 가려내기 위해 가로가 세로보다 더 큰지 확인(if(sizes[i][0] > sizes[i][1]) { ... })하는 형식으로 구별하였고, 가로, 세로 중 큰 값을 longer 변수에, 작은 값은 shorter 변수에 push를 이용해 값을 넣었다.

 

그 이후 longer 변수와 shorter 변수에서 max값을 찾아내(Math.max(...longer)) 최종적으로 곱한 값을 도출하였다.

 

 

Array.prototype.push()

배열의 끝에 하나 이상의 요소를 추가하고, 배열의 새로운 길이를 반환한다.

 

 

Array.prototype.pop()

배열에서 마지막 요소를 제거하고 그 요소를 반환한다.

 

 

Math.max()

입력값으로 받은 0개 이상의 숫자 중 가장 큰 숫자를 반환한다. 배열에서 max값을 찾아낼 때는 '전개구문(spread operator) ...'을 이용해 값을 찾아낼 수 있다.

 

 

 

#2

function solution(sizes) {
    let width = 0;
    let height = 0;
    
    for(let i = 0; i < sizes.length; i++){
        if(sizes[i][0] < sizes[i][1]){
            sizes[i].reverse();
        }
        if(sizes[i][0] > width){
            width = sizes[i][0];
        }
        if(sizes[i][1] > height){
            height = sizes[i][1];
        }
    }
    
    return width * height;
}

값을 찾아낼 때 변수로 모두 담기보다 일치하는 값만 담을 수 없을까를 고민해 보았다. 또한 명함을 가로로 눕혀서 넣는다는 문제 설명을 보고 입력받은 size를 뒤집어서, 어느 변을 가로로 할지 고민할 필요 없게 하면 되지 않을까 하는 생각이 들었다.

 

따라서 들어온 (가로, 세로) 값 중 세로가 가로보다 더 긴 값이 있으면 reverse()를 이용해 배열을 뒤집어 큰 수가 무조건 가로가 되게 만들고, 기존에 변수에 담아있던 값보다 더 크면 변수에 담고, 아니면 넘어가는 형식으로 진행했다.

 

for문을 돌릴 때 let i = 0; i < size.length; i++로 작성하지 않고 let i in sizes로도 작성해 봤었는데, MDN을 찾아보니 예상치 못한 문구가 적혀있었다. for....in은 인덱스의 순서가 중요한 배열에서 반복을 위해 사용할 수 없다고 한다. 그 이유는 배열의 반복이 일관된 순서로 요소를 찾아가지 못할 수 있어 내가 원하는 순서에 따라 인덱스값을 반환하는 것을 보장할 수 없다. 따라서 방문의 순서가 중요한 배열의 반복 시에는 숫자 인덱스를 사용할 수 있는 for, forEach, for...of를 권장한다고 한다. 배열에는 절대 for...in 쓰지 않기!

 

 

Array.prototype.reverse()

배열의 순서를 반전한다. 첫 번째 요소는 마지막 요소가 되며, 마지막 요소는 첫 번째 요소가 된다.

 

 

 

다른 사람들이 작성한 답안

function solution(sizes) {
    const rotated = sizes.map(([w, h]) => w < h ? [h, w] : [w, h]);

    let maxSize = [0, 0];
    rotated.forEach(([w, h]) => {
        if (w > maxSize[0]) maxSize[0] = w;
        if (h > maxSize[1]) maxSize[1] = h;
    })
    return maxSize[0]*maxSize[1];
}

내가 작성했던 두 번째 답처럼 긴 변이 무조건 가로가 되도록 뒤집을 때 map, 화살표 함수, 삼항연산자를 이용해 깔끔하게 표현하셔서 정말 놀랐던 답안이다.

 

forEach는 아직 제대로 공부하지 않았던 부분인데, 각 요소에 대해 실행할 함수([w, h])를 배열에 있는 각 요소에 대해 오름차순으로 한 번씩 실행한다고 한다. 그렇다면 forEach랑 for...of중에 어떤 걸 쓰는 게 더 좋은 걸까. 이 부분에 대해서는 더 공부해 봐야겠다.

 

 

Array.prototype.map()

배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환한다.

 

 

Array.prototype.forEach()

주어진 함수를 배열 요소 각각에 대해 실행한다.

 

 

 


[참고 자료]

https://school.programmers.co.kr/learn/courses/30/lessons/86491?language=javascript 

 

프로그래머스

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

programmers.co.kr

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/for...in

 

for...in - JavaScript | MDN

for...in문은 상속된 열거 가능한 속성들을 포함하여 객체에서 문자열로 키가 지정된 모든 열거 가능한 속성에 대해 반복합니다. (Symbol로 키가 지정된 속성은 무시합니다.)

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map

 

Array.prototype.map() - JavaScript | MDN

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

 

Array.prototype.forEach() - JavaScript | MDN

forEach() 메서드는 주어진 함수를 배열 요소 각각에 대해 실행합니다.

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse

 

Array.prototype.reverse() - JavaScript | MDN

reverse() 메서드는 배열의 순서를 반전합니다. 첫 번째 요소는 마지막 요소가 되며 마지막 요소는 첫 번째 요소가 됩니다.

developer.mozilla.org

 

 

반응형