JavaScript/알고리즘

프로그래머스 푸드파이트 대회 JS ( 중첩for문 )

hihiha2 2023. 7. 11. 15:42

문제 설명

수웅이는 매달 주어진 음식을 빨리 먹는 푸드 파이트 대회를 개최합니다. 이 대회에서 선수들은 1대 1로 대결하며, 매 대결마다 음식의 종류와 양이 바뀝니다. 대결은 준비된 음식들을 일렬로 배치한 뒤, 한 선수는 제일 왼쪽에 있는 음식부터 오른쪽으로, 다른 선수는 제일 오른쪽에 있는 음식부터 왼쪽으로 순서대로 먹는 방식으로 진행됩니다. 중앙에는 물을 배치하고, 물을 먼저 먹는 선수가 승리하게 됩니다.

이때, 대회의 공정성을 위해 두 선수가 먹는 음식의 종류와 양이 같아야 하며, 음식을 먹는 순서도 같아야 합니다. 또한, 이번 대회부터는 칼로리가 낮은 음식을 먼저 먹을 수 있게 배치하여 선수들이 음식을 더 잘 먹을 수 있게 하려고 합니다. 이번 대회를 위해 수웅이는 음식을 주문했는데, 대회의 조건을 고려하지 않고 음식을 주문하여 몇 개의 음식은 대회에 사용하지 못하게 되었습니다.

예를 들어, 3가지의 음식이 준비되어 있으며, 칼로리가 적은 순서대로 1번 음식을 3개, 2번 음식을 4개, 3번 음식을 6개 준비했으며, 물을 편의상 0번 음식이라고 칭한다면, 두 선수는 1번 음식 1개, 2번 음식 2개, 3번 음식 3개씩을 먹게 되므로 음식의 배치는 "1223330333221"이 됩니다. 따라서 1번 음식 1개는 대회에 사용하지 못합니다.

수웅이가 준비한 음식의 양을 칼로리가 적은 순서대로 나타내는 정수 배열 food가 주어졌을 때, 대회를 위한 음식의 배치를 나타내는 문자열을 return 하는 solution 함수를 완성해주세요.

 

🙋‍♀️ 내 생각

이번 문제를 풀때는 스터디를 하면서, 시간제한을 두고 풀었다.

확실히 이렇게 시간제한을 두고 풀어서 그런지, 마음이 급해져서 처음에 문제를 잘못이해해서 조금 헤맸다 🥲

 

테스트코드는 합격하는데 문제제출을 누르면 실패가 생겨서 pass가 되지 않았다.

시간이 얼마 남지 않아서 뭐가 잘못된건지 모르겠어서 코드를 하나 더 짜서 두개의 코드를 만들었으나, 두번째코드 역시 테스트코드만 합격하고 제출하기하면 pass가 안되었다..

 

느낀점은 급할수록 문제를 다시 제대로 읽자였다 ㅎㅎ

제대로 코드를 구현했으나 pass가 안되는 경우는 결국에 문제를 잘못이해했거나, 예외적인 부분들을 처리하지 못해서이다.

 

우선 내가 짠 2개의 코드는 아래와 같다. 

// 코드1
function solution(food){
    const num = food.map((v)=>Math.floor(v/2))
    const [water, cal1, cal2, cal3] = [...num]
    
    const front = '1'.repeat(cal1) + '2'.repeat(cal2) + '3'.repeat(cal3)
    const back = '3'.repeat(cal3) + '2'.repeat(cal2) + '1'.repeat(cal1)
    
    return front + water + back
}

food를 돌면서 각 요소를 2로 나누고 Math.floor()를 통해서 각 값의 몫만을 구했다. 몇회 반복할지는 이 몫의 값과 같다.

그런다음, 배열의 구조분해할당을 통해서 구한 몫을 변수에 담았다. 이 과정은 굳이 안해도 되지만, 코드의 가독성을 위해서 진행하였다.

 

앞과 뒤가 같은 코드가 반복되기 때문에, 앞과 뒤를 나누어서 더해야겠다고 생각했다.

뒷부분은 앞을 뒤집으면 되기때문에 중간 0을 기준으로 앞뒤를 나누었다.

 

그리고 repeat()을 통해서 각각의 값을 몇회 반복할지를 정했다.

back도 마찬가지로 처리하였다.

 

마지막으로 각각의 값을 더하고 반환한다.

 

이렇게 하면 테스트코드는 모두 통과하지만, 최종적으로 답을 제출하면 실패하는 부분이 생긴다.

이유는 제한사항에 있는 저 부분때문이다.

나는 배열의 구조분해할당을 통해서 cal1, cal2, cal3와 같이 경우를 나눠서 값을 담았는데, 이건 food의 길이가 4라는 전제하에 짠 코드이다. 

food의 길이는 2-9까지이고 정해져있지 않기때문에 저런식으로 food의 길이가 정해진것처럼 풀면 안된다.

 

 

위의 코드가 pass가 안되어서 바로 약간 방식을 바꿔서 풀었지만, 위와 같은 이유로 이 코드도 되지 않았다.

// 코드2
function solution(food){
    const num = food.map((v,i)=>Math.floor(v/2))
    const [water, cal1, cal2, cal3] = [...num]
    
    const f1 = Array(cal1).fill(1)
    const f2 = Array(cal2).fill(2)
    const f3 =Array(cal3).fill(3)
    
    const front = [...f1,...f2,...f3]
    const back = [...f3,...f2,...f1]
    
    return front.join('')+String(water)+back.join('')
}

이 코드도 역시 food의 길이를 정해서 풀어서 안되었는데, 급할수록 문제를 다시 제대로 읽어야겠다는 생각이 들었다. 😇

 

 

 

✅ 내 코드

function solution(food){
    temp = ''
    for(i=1; i<food.length; i++){
        let count = Math.floor(food[i]/2)
        
        for(j=0; j<count; j++){
            temp += i.toString()
        }
    } return temp + "0" + [...temp].reverse().join('')
}

문제를 다시 제대로 이해하고 짠 코드이다.

 

위의 두 코드와의 차이점food의 길이를 4로 픽스하지 않았다는 점이다.

food의 길이를 food.length로 정해서 상황에 따라 다른 길이를 갖도록 한다.

 

중첩for문을 통해서 food의 길이만큼 food배열을 돌면서 특정한 동작을 반복한다.

맨앞의 물을 빼고, 1번째 인덱스부터는 1,2,3,4,...이런식의 값이 정해지고

food의 각 요소를 2로 나눈 몫만큼 수를 반복한다. 만일 인덱스 1의 몫이 2이라면 11과 같은 값이 나올 것이다.

(맨앞은 물이라서 제외하므로 i=1부터 시작한다)

 

중첩for문의 안에서는 i를 string으로 바꿔서 더하는 행위를 반복한다. 

i를 몇회 반복할지를 정하는 것이기때문에 j의범위를 0부터 count보다 작게해서 count의 길이만큼의 i를 temp에 더한다.

 

여기까지 진행하고 temp의 값을 테스트를 통해서 확인해보면 아래와 같다.

 

temp에는 앞부분의 값만이 담겨있다. 

 

이제 물과 뒷부분도 더해준다.

 

temp를 뒤집기위해서 reverse()를 사용하는데, reverse()는 배열에만 사용할 수 있기때문에 ...전개연산자와 []를 통해서 temp를 배열로 만든뒤에 메서드를 이용한다. reverse된 값은 각각의 요소가 나뉘어져있는 배열이기 때문에 다시 join("")을 통해서 하나의 문자열로 만들고나서 앞부분과 0과 더한다.

 

 temp + "0" + [...temp].reverse().join("") 

앞부분 + "0" + 뒷부분

 

 

 

 

💻  다른사람 코드중에 배울 것 1

function solution(food) {
    let res = '';
    for (let i = 1; i < food.length; i++) {
        res += String(i).repeat(Math.floor(food[i]/2));
    }

    return res + '0' + [...res].reverse().join('');
}

기본적인 틀은 같지만 중첩for문을 사용하지 않고 풀었다.

 

i를 string으로 바꾼 다음, repeat을 통해 몇번 반복할지를 정한다. 

repeat할 횟수는 Math.floor를 통해서 food의 각 요소를 2로 나눈 값의 몫이다.

 

중첩 for문을 사용하지 않음으로써 코드가 한결 간결해졌다.

 

 

 

💻  다른사람 코드중에 배울 것 2

function solution(food) {
    let player1 = [];
    let player2 = [];
    let repeat = 0;
    for (let i = 1; i <= food.length; i++) {
        if (food[i] > 1) {
            repeat = Math.floor(food[i] / 2);
            for (let j = 0; j < repeat; j++)
                player1.push(i)
            for (let k = 0; k < repeat; k++)
                player2.unshift(i)
        }
    }
    player1.push(0);
    player1 = player1.concat(player2);
    return player1.join("");
}

player1과 plater2를 각각 빈배열에 담아서 push와 unshif를 통해서 배열의 끝과 끝에 수를 추가하는 방식으로 풀었다.

문제에 나와있는 것을 각 player들이 어떻게 행동하고 있는지를 직관적으로 알 수 있는 코드가 같아서 공부해보았다.

 

우선 player1과 player2라는 빈배열을 각각 만들어준다.

그리고 repeat이라는 변수에 숫자 0으로 초기화를 한다.

 

for문을 통해서 i=1부터 food.length의 길이만큼을 돈다. (i=1인 이유는 위에 말했듯이 0번째 인덱스는 물이기때문에)

(나는 for문의 범위는 i<food.length이고 if문안의 범위는 food[i]>=1라고 생각해서 위의 범위는 잘 이해하지 못했다. 이렇게 풀어도 저렇게 풀어도 코드가 통과가된다. 정확히 어떤 차이인지는 모르겠다)

 

if 물이 아니라면, player1에 repeat만큼 push를 통해서 앞부터 추가하고,

player2에 repeat만큼 unshitf를 통해서 뒤부터 추가한다.

 

그런 다음 마지막에는 player1에 0을 push한 다음 concat을 통해 두 값을 연결하고 join('')을 통해서 하나의 문자열로 만든다.

 

코드가 직관적이고 문제에서 각 player가 어떻게 진행하고 있는지를 쉽게 알 수 있다.

 

 

 

 

🙋‍♀️ 내 생각

문제를 빨리풀려고 하다보니 제한사항을 제대로 충족하지 못한 코드만 2개를 만들었다 ㅎㅎ 

확실히 문제를 제대로 읽고 문제에서 요구하는 사항이 무엇인지, 예외적으로 처리해주어야 할 부분이 무엇인지를 잘 파악하는 것이 중요하다는 것을 느꼈다. 생각하는 바를 코드로 구현할 수 있다고 하더라도 생각의 방향자체가 틀린다면 계속해서 틀린 코드를 만들기 때문이다.

 

push와 unshit를 통해서 배열의 뒤부터, 또 앞부터 값을 추가하는 방법은 생각지 못했는데 앞으로 다른 문제를 풀때 이런 방법도 활용하면 좋을것같다는 생각이 들었다.