스터디 기록 11

유아현·2022년 11월 30일
0

Study

목록 보기
12/27
post-thumbnail

오늘의 스터디 문제 목록

1. 체육복

점심시간에 도둑이 들어, 일부 학생이 체육복을 도난당했습니다. 다행히 여벌 체육복이 있는 학생이 이들에게 체육복을 빌려주려 합니다. 학생들의 번호는 체격 순으로 매겨져 있어, 바로 앞번호의 학생이나 바로 뒷번호의 학생에게만 체육복을 빌려줄 수 있습니다. 예를 들어, 4번 학생은 3번 학생이나 5번 학생에게만 체육복을 빌려줄 수 있습니다. 체육복이 없으면 수업을 들을 수 없기 때문에 체육복을 적절히 빌려 최대한 많은 학생이 체육수업을 들어야 합니다.
전체 학생의 수 n, 체육복을 도난당한 학생들의 번호가 담긴 배열 lost, 여벌의 체육복을 가져온 학생들의 번호가 담긴 배열 reserve가 매개변수로 주어질 때, 체육수업을 들을 수 있는 학생의 최댓값을 return 하도록 solution 함수를 작성해주세요.

실패 코드 1

function solution(n, lost, reserve) {
  // 전체 학생 n, 도난당함 lost, 기부학생번호 reserve
  // 여벌을 가지고 있는 학생이 도난당하면 빌려줄 수 없으므로 
  // lost와 reserve에 동시에 존재하면 reserve에 해당 학생 번호를 지워줌
  
  // 1.  lost와 reserve 둘 다 번호가 있으면 reserve에서 제거해 줘야 됨
  //     여벌을 가졌지만 도난당한 학생을 제외한 배열을 다시 reserve에 재할당
  reserve = reserve.filter((ele) => !lost.includes(ele)); 
  
  
  // 2.  여벌을 가지지도 않고 도난당하지도 않은 학생과 여벌을 가진 학생들은 
  //     체육수업을 들을 수 있으므로 도난당한 학생의 수만 전체 학생 수에 - 해 준다
  let result = n - lost.length;
  
  // 3.  이미 체육복은 받은 학생이 여벌을 가진 학생에게 또 받으면 안 되기 때문에 
  //     받을 수 있는 학생들을 모두 push 후 set으로 중복 제거
  //    그리고 중복제거한 배열의 길이를 result와 더한다.
  let arr = [];

  for (let i = 0; i < reserve.length; i++) {
    for (let j = 0; j < lost.length; j++) {
      if (reserve[i] - 1 === lost[j]) {
        arr.push(lost[j]);
        if (reserve[i] + 1 === lost[j]) {
          arr.push(lost[j]);
        }
      }
    }
  }

  // arr 중복 제거
  const uniqueArr = [...new Set(arr)];
  result += uniqueArr.length;
  return result;
}
  • 처음 작성했던 코드... 문제를 제대로 이해하지 못한 티가 팍팍 난다. 처음에 여벌을 가졌지만 도난당한 학생들을 reserve에 지우기만 하고 lost엔 지워주지도 않은 얼척없는 코드... 반성. 테스트 케이스를 대거 틀리곤 코드를 새로 짰다

실패 코드 2

function solution(n, lost, reserve) {
  //lost와 reserve 둘 다 번호가 있으면 reserve과 lost에서 제거해 줘야 됨
  // 여벌을 가진 사람 중 도난당한 학생 빼줌
  reserve = reserve.filter((ele) => !lost.includes(ele));
  // 도난당한 사람 중에서 여벌을 가졌다면 빼줌
  lost = lost.filter((ele) => !reserve.includes(ele));

  for (let i = 0; i < reserve.length; i++) {
    for (let j = 0; j < lost.length; j++) {
      // 앞, 뒤 번호 학생한테 줄 수 있으면 해당 학생은 0으로 바꾼다
      if (reserve[i] - 1 === lost[j] || reserve[i] + 1 === lost[j]) {
        lost[j] = 0;
        break;
      }
    }
  }
  // 도난당한 학생들의 배열에서 받을 수 있는 학생들은 0으로 바뀌었으므로
  // 전체 학생 n에 체육복을 받지 못한 학생/ 0이 아닌 학생의 명 수를 구해서 빼준다

  return n - lost.filter((ele) => ele !== 0).length;
}
  • 다시 작성한 코드, 이 코드도 코드 실행만 통과하고 테스트 케이스에서 막혔다 여벌을 가졌지만 도난당한 학생들을 reserve에 제거해 주고 옷은 받아야 하는(도난당한 사람)에도 여별을 가졌지만 도난당해서 본인 것밖에 없을 거라고 생각하고 lost에 빼줬다. 그리고 for문을 돌면서 앞 뒤 학생들에게 lost 학생들이 체육복을 받으면 해당 인덱스 값을 0으로 바꾸고 최종적으로 0이 아닌 학생들(옷을 한 벌이라도 가진 학생들)의 수를 리턴해 줬다. 근데 이것도 테스트 케이스가 꽤나 많이 실패했다...

테스트 케이스 실패 이유

  • 만약 3이라는 학생이 여벌을 가지고 있고, 2, 4는 체육복을 도난당했다고 가정해보자. 3 학생은 2와 4 학생에게 체육복을 빌려줄 수 있지만 3이 가지고 있는 체육복이 2개인지 3개인지 모른다,,, 이러한 이유 때문에 실패했던 것 같아 해당 인덱스에 할당해 주는 방식을 재활용하여 다시 풀어보았다.

성공 코드

function solution(n, lost, reserve) {
  // 전체 학생이 각자 가진 체육복 수를 1로 설정한다.
  const uniform = Array(n).fill(1); // 1 1 1 1 1
  // 도난당한 학생들의 체육복 수를 0으로 설정, 인덱스로 접근하기 때문에 학생 번호 - 1로 해야 됨
  lost.map((lost) => {
    uniform[lost - 1] = 0;
  });

  // 여벌을 가진 학생들은 +1 해 준다.
  reserve.map((reserve) => {
    uniform[reserve - 1] += 1;
  }); // 1 0 2 0 1

  for (let i = 0; i < n; i++) {
    // 체육복이 0개인 학생이 앞의 학생한테 체육복을 받으면.
    // 그 학생의 체육복 수를 +1로 하고 앞의 학생은 2개에서 하나를 줬기 때문에 -1을 해 준다.
    if (uniform[i] === 0 && uniform[i - 1] === 2) {
      uniform[i] += 1; // 1 0 1 1 1
      uniform[i - 1] -= 1;
    }
    // 체육복이 0개인 학생이 뒤의 학생한테 체육복을 받으면.
    // 그 학생의 체육복 수를 +1로 하고 앞의 학생은 2개에서 하나를 줬기 때문에 -1을 해 준다.
    else if (uniform[i] === 0 && uniform[i + 1] === 2) {
      uniform[i] += 1;
      uniform[i + 1] -= 1;
    }
  }

  // 가진 체육복이 1개 이상인 학생들의 수를 리턴
  return uniform.filter((ele) => ele > 0).length; // 1 0 1 1 1 => 4
}
  • 위의 코드와 같이 모든 학생의 체육복 수를 설정해 두고, 잃어버린 학생은 0으로, 여벌을 가진 학생을 +1로 해서(여벌을 가진 학생은 2가 됨) 앞의 학생과 뒤의 학생에게 주게 되면 그 학생은 체육복 하나가 사라지는 것이므로 -1을 해 주고 잃어버린 학생은 체육복을 받은 것이니까 +1을 해 줘서 마지막엔 체육복이 0 이상인 학생들의 수를 구해줬다.
//! 1. lost와 reserve 겹치는 요소 제거
  for (let i = 0; i < lost.length; ) {
    if (reserve.includes(lost[i])) {
      reserve.splice(reserve.indexOf(lost[i]), 1);
      lost.splice(i, 1);
    } else i++;
  }
  • 스터디원 민혁님 코드,,, 겹치는 요소를 제거하는 방법을 for문으로 하셨는데 방법이 너무 신기했다. 증감을 else에서 처리하는 것은 첨 봤어서 색다른 방법인 것 같아 너무 좋았다!!

2. 푸드 파이트 대회

수웅이는 매달 주어진 음식을 빨리 먹는 푸드 파이트 대회를 개최합니다. 이 대회에서 선수들은 1대 1로 대결하며, 매 대결마다 음식의 종류와 양이 바뀝니다. 대결은 준비된 음식들을 일렬로 배치한 뒤, 한 선수는 제일 왼쪽에 있는 음식부터 오른쪽으로, 다른 선수는 제일 오른쪽에 있는 음식부터 왼쪽으로 순서대로 먹는 방식으로 진행됩니다. 중앙에는 물을 배치하고, 물을 먼저 먹는 선수가 승리하게 됩니다.
이때, 대회의 공정성을 위해 두 선수가 먹는 음식의 종류와 양이 같아야 하며, 음식을 먹는 순서도 같아야 합니다. 또한, 이번 대회부터는 칼로리가 낮은 음식을 먼저 먹을 수 있게 배치하여 선수들이 음식을 더 잘 먹을 수 있게 하려고 합니다. 이번 대회를 위해 수웅이는 음식을 주문했는데, 대회의 조건을 고려하지 않고 음식을 주문하여 몇 개의 음식은 대회에 사용하지 못하게 되었습니다.
예를 들어, 3가지의 음식이 준비되어 있으며, 칼로리가 적은 순서대로 1번 음식을 3개, 2번 음식을 4개, 3번 음식을 6개 준비했으며, 물을 편의상 0번 음식이라고 칭한다면, 두 선수는 1번 음식 1개, 2번 음식 2개, 3번 음식 3개씩을 먹게 되므로 음식의 배치는 "1223330333221"이 됩니다. 따라서 1번 음식 1개는 대회에 사용하지 못합니다.
수웅이가 준비한 음식의 양을 칼로리가 적은 순서대로 나타내는 정수 배열 food가 주어졌을 때, 대회를 위한 음식의 배치를 나타내는 문자열을 return 하는 solution 함수를 완성해주세요.

function solution(food) {
  // (선수1) 1 2 2 3 3 3 물 3 3 3 2 2 1 (선수2)
  // [...선수1의 음식 배치, 0, ...선수 2의 음식 배치]으로 합치고 join('')
    
  // 1. 선수1의 음식 배치 구해서 선수2 때 그대로 활용해 reverse만 시켜 주기
  // 2. map을 이용해 각 요소의 나누기 2로 나온 몫으로 변환한다. => 각 선수에게 줄 수 있는 음식양
  // [1, 3, 4, 6] => [0, 1, 2, 3] 물, 첫번째음식, 두번째음식, 세번째음식
  // 3. 배열의 인덱스 === 음식 순서이므로 음식의 양만큼 인덱스가 반복되면서 새로운 배열에 push
  food = food.map((ele) => {
    return (ele = Math.floor(ele / 2));
  });
 // 선수 1 음식 배치
  let arr1 = [];
    
  for (let i = 0; i < food.length; i++) {
    // [0, 1, 2, 3]
    if (!!food[i]) { // 들어온 음식의 수가 0이라면 false이므로 실행하지 않음
      for (let j = 1; j <= food[i]; j++) {
        arr1.push(i); // 배열의 인덱스 === 음식 순서
      }
    }
  }

  // 선수2의 음식 배치, 선수1의 음식 배치 활용
  let arr2 = [...arr1];
  arr2.reverse();

  const result = [...arr1, 0, ...arr2];

  return result.join('');
}
  • 문제 보자마자 선수 1의 음식 배치 배열 만들어 주고 선수2에 reverse 해 준 다음에 새 배열에 spreed 연산자 써서 리턴해 줘야지~ 했던 문제 접근하는 방법은 매우 쉬웠다.
function solution(food) {
  food.reverse()
  let str = "0"
  food.forEach((element,index) => {
    const foodCount = Math.floor(element/2);
    const foodStr = String(food.length-(index+1)).repeat(foodCount);
    str = foodStr+str+foodStr
  });
  return str
}
  • 개인적으로 참 깔끔하고 예쁘다고 생각한 지원님 코드 repeat로 각 음식에 맞는 음식을 반복 시켜 주고 그게 다니 str이 돼서 반복하는 방식도 좋다고 생각했다!
function solution(food) {

  let tmp = [0];
                                                            //[1, 7, 1, 2]
  for(let i=food.length; i>=1; i--){                        //3번인덱스->2번인덱스->1번인덱스
    if(food[i] >= 2){
        food[i] = Math.floor(food[i]/2);                    //[1, 7, 1, ☑️1] 로 변경됨
          let repeatStr = String(i).repeat(food[i]);         //인덱스 3을 '3'으로바꾸고 ☑️1 만큼반복해서 문자열변수에 저장
          tmp.unshift(repeatStr)                             //언쉬프트,푸시하면 tmp는 ['3',0,'3'] 이 된다. 이걸반복.
          tmp.push(repeatStr)
    }
      else continue;
  }
  return tmp.join('');
}
  • 접근법이 너무 신박했던 승연님 코드! 흔히 쓰던 push와 unshift를 여기서 볼 줄이야... 선수1과 선수2는 양쪽에서 시작하므로 [선수1] 1 2 3 [0] 3 2 1 [선수2] 이 구조니까 0을 기준으로 선수에 맞게 값을 넣어 준 게 너무 좋았고 한눈에 들어왔다!

3. 완주하지 못한 선수

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

function solution(participant, completion) {
  // 처음 filter와 includes를 사용하여 차집합을 구해 풀려고 했으나 동명이인이 있는 경우가 있어 사용하지 못함
  // 마라톤에 참여한 선수의 이름이 담긴 배열 participant
  // 완주한 선수들의 이름이 담긴 배열 completion
  // 참여 선수와 완주 선수 정렬 후, 서로의 인덱스가 같지 않으면 해당 인덱스 값을 리턴
  participant.sort();
  completion.sort();
  let idx = 0;
  while (participant[idx] === completion[idx]) {
    idx++;
  }
  return participant[idx];
}

solution(["leo", "leo", "kiki", "eden"], ["leo", "eden", "kiki"]);
  • 처음에는 엥? 문제가 왜 이렇게 쉽지 했는데 엄청 간단하게 풀린 문제는 아니었다. 체육복에 비하면 쉽긴 하지만 ^^... 알고보면 무지 단순한 문제! 정렬 후 차례대로 명단을 훑는데 다른 부분이 있다면 그 인덱스를 반환하는 방식으로 풀었다.
function solution(participant, completion) {
  const obj = {};
  for (let el of participant) {
    if (el in obj) obj[el]++;
    else obj[el] = 1;
  }
  for (let el of completion) {
    if (obj[el] === 1) delete obj[el];
    else obj[el]--;
  }
  return Object.keys(obj)[0];
}
  • 객체로 접근해서 푸신 민혁님의 방법 아 요런 방법도 있었군 하면서 흥미롭게 봤다!

내가 풀었던 걸 다시 보니까 코드도 좀 더럽고 특히 의사코드도 주절주절 쓸데없는 말을 많이 적어 놓은 것 같다 ㅋㅋㅋ 간결하게 작성하려고 쪼끔만 더 노력해 보자...! 그리고 부뜩 (생각보다 문제가 어렵지 않아서 그랬나?) 코딩 실력이 아주 조금 늘었다는 기분이 들었다....! 아무렇지도 않게 filter와 map을 사용하려는 모습에 첨엔 이해도 못했는데..... 많이 성장했구나 싶었다 앞으로도 파이팅이야 ~~

0개의 댓글