TIL. 완전 탐색(Brute Force) 알고리즘 문제풀이

seul_velog·2023년 3월 14일
0

TIL_algorithm

목록 보기
15/26
post-custom-banner

멘토링

문제 설명

현수네 반 선생님은 반 학생들의 수학점수를 향상시키기 위해 멘토링 시스템을 만들려고 합니 다. 멘토링은 멘토(도와주는 학생)와 멘티(도움을 받는 학생)가 한 짝이 되어 멘토가 멘티의 수학공부를 도와주는 것입니다. 선생님은 M번의 수학테스트 등수를 가지고 멘토와 멘티를 정합니다. 만약 A학생이 멘토이고, B학생이 멘티가 되는 짝이 되었다면, A학생은 M번의 수학테스트에서 모두 B학생보다 등수가 앞서야 합니다. M번의 수학성적이 주어지면 멘토와 멘티가 되는 짝을 만들 수 있는 경우가 총 몇 가지 인지 출력하는 프로그램을 작성하세요.

입력설명
첫 번째 줄에 반 학생 수 N(1<=N<=20)과 M(1<=M<=10)이 주어진다. 두 번째 줄부터 M개의 줄에 걸쳐 수학테스트 결과가 학생번호로 주어진다. 학생번호가 제일 앞에서부터 1등, 2등, ...N등 순으로 표현된다. 만약 한 줄에 N=4이고, 테스트 결과가 3 4 1 2로 입력되었다면 3번 학생이 1등, 4번 학생이 2등, 1번
학생이 3등, 2번 학생이 4등을 의미합니다.

출력설명
첫 번째 줄에 짝을 만들 수 있는 총 경우를 출력합니다.
예를 들어 [[3,4,1,2],[4,3,2,1],[3,1,4,2]] 가 주어졌을 때 멘토 멘티가 될 수 있는 짝은 (3,1), (3,2), (4,2) 3가지의 경우이다.

▣ 입출력 예

arrreturn
[[3,4,1,2],[4,3,2,1],[3,1,4,2]]3
[[1,2,3,4],[3,1,2,4],[3,2,4,1]]2

풀이

✍️ solution

function solution(test) {
  let answer = 0;
  let m = test.length; // 1)
  let n = test[0].length;
  for (let i = 1; i <= n; i++) { // 2)
    for (let j = 1; j <= n; j++) { // 3)
      let count = 0; 
      for (let a = 0; a < m; a++) { // 4)
        let ii = 0; // 5)
        let jj = 0; 
        for (let b = 0; b < n; b++) { // 6)
          if (test[a][b] === i) ii = b; // 7)
          if (test[a][b] === j) jj = b; // 7-1) 
        }
        if (ii < jj) count++; // 8) 
      }
      if (count === m) answer++; // 9)
    }
  }
  return answer;
}

let arr1 = [
  [3, 4, 1, 2],
  [4, 3, 2, 1],
  [3, 1, 4, 2],
];
console.log('👉 answer: ', solution(arr1)); // 3
// (3,1),(3,2),(4,2) 3가지의 경우 존재

let arr2 = [
  [1, 2, 3, 4],
  [3, 1, 2, 4],
  [3, 2, 4, 1],
];
console.log('👉 answer: ', solution(arr2)); // 2
// (2,4),(3,4) 2가지의 경우 존재

🤔 어떻게 풀어볼까?

  1. 모든 학생이 멘토와 멘티 짝이 될 수 있는 경우의 수를 따진다.
    → 여기서는 4명의 학생이 존재하므로 4*4 = 16 이 된다. (본인이 본인스스로 멘토멘티가 된다는 점도 포함하고 있지만 이후 조건문을 통해 배제하기로 한다.)

  2. 2중 for 문을 통해 멘토를 i 멘티를 j 로 설정하고 (i,j) 형태로 모든 경우의 수를 판별한다.
    ❗️ 이 때의 2중 for 문은 오로지 멘토와 멘티 학생 자체를 가리키게 된다.
    ex.) (3,1)일 경우 학생3과 학생1이 된다.

  3. 2중 for 문 안에서 또 다시 2중 for 문을 돌리는데 여기서는 학생의 위치, 즉 등수를 가리키게 된다.
    ex.) test[0][2] 는 test의 0번째 테스트 내의 2번째 성적 위치
    → 이때 이 성적 위치를 통해 멘토 ij 보다 등수가 높은지(숫자가 낮은지)를 체크하여 count++ 를 한다.
    → 이 for 문 안에서 모든 테스트에서 등수가 높다면(숫자가 낮다면) 멘토 멘티가 될 자격이 갖추어졌으므로 최종적으로 answer++ 를 한다.


  • 1) m 은 총 테스트의 수, n 은 총 학생의 수 이다. n 의 경우 test의 몇번째 인덱스에 접근해도 상관 없지만 총 몇명의 학생이 있는지 모르므로 [0] 으로 지정하자.
  • 2) 멘토가 되는 학생을 선택
  • 3) 멘티가 되는 학생을 선택
  • 4) m번의 테스트 결과 모든 테스트에서 멘토의 점수가 멘티보다 높은 경우를 count 로 체크하고, answer 를 구하는 for문에 들어간다.
    이 때, 테스트의 수만큼 돌아야 하므로 (let a = 0; a < m; a++) 로 셋팅한다.
  • 5) i번 학생과 j번 학생의 등수 ii, jj 저장(초기화)
  • 6) b 는 등수로 생각하자. 학생수만큼 돌아야 하므로 (let b = 0; b < n; b++) 로 셋팅한다.
    이 for문에서 i번학생과 j번학생의 등수가 결정된다.
  • 7) a번째 테스트에, b 위치에 i학생이 있으면 ii = b 로 i학생의 등수를 재할당한다.
  • 7-1) a번째 테스트에, b 위치에 j학생이 있으면 jj = b 로 j학생의 등수를 재할당한다.
  • 8) 멘토가 멘티보다 등수가 높을경우 증가한다.
  • 9) 가능한 모든 멘토멘티 짝의 경우의 수를 구하고 해당 짝이 M번의 테스트에서 모든 테스트를 통과할 경우만 증가시킨다.
    예를들어 테스트의 갯수 m 만큼 count 가 증가하여 count === m 이라면 멘토와 멘티의 모든 경우의 수 조건을 만족했다는 뜻이므로 answer 를 증가시킨다.

✍️ 처음에는 2중 for문만을 생각해서 어떻게 풀어야 할지 한참 고민했다.
멘토와 멘티가 될 학생을 타겟으로 잡는 경우의 수를 갖는 2중 for문에 이어서 이 학생들의 위치, 즉 등수를 타겟으로 잡는 for문을 잡고 그 위에 한번 더 모든 테스트에서의 등수를 체크해야 하므로 여러 for문을 고려해야 하는 문제였던 것 같다. 🧐🔥
결국 오랫동안 고민했던 부분이 이 부분이었고 여기가 핵심이었다.

if (test[a][b] === i) ii = b; // a번째 테스트에, b 위치에 i학생이 있으면 ii = b
if (test[a][b] === j) jj = b; // a번째 테스트에, b 위치에 j학생이 있으면 jj = b

여기서 ij 는 단순 학생 자체를 의미하고 그 학생들이 갖게되는 경우의 수 i,j 즉, (3,1) ... 과 같은 포지션이 된다.
이때 test[a][b]=== 을 통해 현재 i학생(3) 과 j학생(1)의 등수를 b 를 통해서 알 수 있다. 그렇게 알게 된 학생 각각의 등수를 iijj 할당하고 비교하여 멘토 멘티가 될 자격이 있는지 서로의 등수를 비교하여 체크한다. 😎 ✅

profile
기억보단 기록을 ✨
post-custom-banner

0개의 댓글