티스토리 뷰

반응형

 

 

요즘 노마드 코더로 챌린지를 하면서 백준 문제를 풀고 있다. 

(푼 문제들은 깃헙에 올린다. https://github.com/AlgoRoots)

어차피 몇 번 더 풀어볼 생각이라 깃 헙에만 올리고 따로 적지는 않으려고 했는데 

그날 푼 문제 중 가장 나에게 유용했던 혹은 새로운 것을 많이 알았던 문제는 정리 겸 올리기로 했다.

 

오늘의 문제는 4344번이다. 

 

 

 

문제

대학생 새내기들의 90%는 자신이 반에서 평균은 넘는다고 생각한다. 당신은 그들에게 슬픈 진실을 알려줘야 한다.

입력

첫째 줄에는 테스트 케이스의 개수 C가 주어진다.

둘째 줄부터 각 테스트 케이스마다 학생의 수 N(1 ≤ N ≤ 1000, N은 정수)이 첫 수로 주어지고, 이어서 N명의 점수가 주어진다. 점수는 0보다 크거나 같고, 100보다 작거나 같은 정수이다.

출력

각 케이스마다 한 줄씩 평균을 넘는 학생들의 비율을 반올림하여 소수점 셋째 자리까지 출력한다.

 

 

 

A. shift(), reduce()를 사용하지 않는 방법  


const fs = require("fs");
const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
let input = fs.readFileSync(filePath).toString().trim().split("\n");

const testCount = +input[0];
const testCases = input.slice(1);

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores[0];
    let sum = 0;
    let overAverageCnt = 0;

    for (let j = 1; j <= N; j++) {
      sum += scores[j];
    }
    const average = sum / N;

    for (let k = 1; k <= N; k++) {
      if (scores[k] > average) {
        overAverageCnt++;
      }
    }
    console.log(((overAverageCnt / N) * 100).toFixed(3) + "%");
  }
}

 

 

 

1. 우선 input의 첫번 째 줄을 testCount라 선언하고 숫자로 바꿔 주었다.

그리고 나머지 줄을 slice()를 사용하여 잘라주고 testCases라고 선언하였다. 

 

const testCount = +input[0];
const testCases = input.slice(1);

 

 

 

 

2. 각 줄의 배열들을 score라고 지정했는데, 그 과정에서 각 줄의 문자를 숫자로 바꾸고, 띄어쓰기 기준으로 나눠주는 과정이 필요했다. 띄어쓰기는 split(" ") 숫자는. map(Number)로 각각의 element를 숫자로 바꿔준다. 각 줄을 실행해야 하기 때문에 for문을 돌려준다.

 

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores[0];
  }
}

그럼 scores가 각 줄에 아래처럼 나열이된다. 

Before  After 
[
  '5 50 50 70 80 100',
  '7 100 95 90 80 70 60 50',
  '3 70 90 80',
  '3 70 90 81',
  '9 100 99 98 97 96 95 94 93 91'
]​
[ 5, 50, 50, 70, 80, 100 ]
[ 7, 100, 95, 90, 80,  70, 60, 50]
[ 3, 70, 90, 80 ]
[ 3, 70, 90, 81 ]
[ 9, 100, 99, 98, 97, 96,  95, 94, 93, 91]

각 줄의 첫번째 원소를 N 으로 변수를 지정해준다. 

 

 

 

3.  평균을 구하기 위해 각 scores들의 합을 sum, 평균을 넘는 점수를  overAverageCnt이라 선언하고 0으로 초기화해준다. 

 

그리고 각 줄의 점수의 합을 구하는 for문을 적는다. 그럼 합(sum)이 나온다. 

for문으로 나온 sum들을 평균을 구하기 위해 상수변수 const average를 선언해 평균을 구한다. 

 

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores[0];
    let sum = 0;
    let overAverageCnt = 0;

    for (let j = 1; j <= N; j++) {
      sum += scores[j];
    }
    const average = sum / N;
    // console.log(scores);
    // console.log("sum: ", sum);
    // console.log("average: ", average);

    }

 

헷갈리면 console.log();를 출력해가며 본다. 

 

 

 

 

4. 평균을 넘는 점수들을 구하고 있으면 overAverageCnt값을 1씩 증가시킨다. 

 

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores[0];
    let sum = 0;
    let overAverageCnt = 0;

    for (let j = 1; j <= N; j++) {
      sum += scores[j];
    }
    const average = sum / N;
    
    for (let k = 1; k <= N; k++) {
      if (scores[k] > average) {
        overAverageCnt++;
      }
    }
  }
}

 

 

 

 

5. overAverageCnt를 %로 나타내기 위해 수식을 집어넣고 콘솔에 출력해준다.

 

** 마지막에 평군을 소수점 셋째 자리까지 끊어야 하는데 이는 toFixed()를 사용하면 된다. 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed

 

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores[0];
    let sum = 0;
    let overAverageCnt = 0;

    for (let j = 1; j <= N; j++) {
      sum += scores[j];
    }
    const average = sum / N;

    for (let k = 1; k <= N; k++) {
      if (scores[k] > average) {
        overAverageCnt++;
      }
    }
    console.log(((overAverageCnt / N) * 100).toFixed(3) + "%");
  }
}

 


B. shift(), reduce()를 사용한 방법

 

shift() 는 배열의 첫 번째 요소를  제거해주면서 그 요소를 리턴해준다. 

 

sores [0]의 값은 score.shift(); 한 값과 같다. 

 

reduce() 메서드는 각 요소를 순회하며 callback함수의 실행 값을 누적하여 하나의 결괏값을 반환한다. 

 

찾아보니 보통 reduce() 가르칠 때 모든 요소의 합을 구할 때로 많이 예를 드시는 것 같다. 

하지만 이 함수로 요소의 중복 횟구나 평균, 중복요소 제거 등 더 많은 일을 할 수가 있다. 

이 함수는 나중에 따로 포스팅하겠다. 

 

아무튼, reduce()는 4개의 인수를 가진다. 

  1. accumulator(previousValue) - accumulator는 callback함수의 반환값을 누적 (콜백이 첫 번째 호출이면서 초기값을 제공하는 경우에는 초기값이 accumulator가 된다.) 
  2. currentValue - 배열의 현재 요소
  3. index(Optional) - 배열의 현재 요소의 인덱스 (초기값을 제공한 경우에 0, 아니면 1부터 시작한다)
  4. array(Optional) - reduce()를 호출한 배열

3 번에 하이라이트 부분은 이 메서드의 기능을 찾아보며 헷갈렸던 부분이었다. 다음에 쓸 때도 잊지 말고 꼭 써야겠다. 

 

reduce()는 초기값을 제공하지 않으면 index가 1부터 시작한다. 즉 첫 번째 index 0을 건너뛴다는 말이다. 

 

나는 scores를 index 0자리부터 testcase의 length개수까지 합해야 했다.  따라서 reduce()에 index인자를 넣어 초기값을 설정해주는 과정이 들어가야 한다. 

 

 sum = scores.reduce((acc, cur) => acc + cur, 0 );

 

참고로 mdn에서도 초기값을 제공하지 않을 시에 출력 가능한 형식이 여러개가 되므로 초기값을 제공하는 편이 더 안전하다고 한다. 

 

const fs = require("fs");
const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
let input = fs.readFileSync(filePath).toString().trim().split("\n");

const testCount = +input[0];
const testCases = input.slice(1);

solution(testCount, testCases);

function solution(C, testCases) {
  for (let i = 0; i < testCases.length; i++) {
    let scores = testCases[i].split(" ").map(Number);
    const N = scores.shift();
    let sum = 0;
    let overAverageCnt = 0;

    sum = scores.reduce((acc, cur) => acc + cur, 0);
    const average = sum / N;

    for (let k = 1; k <= N; k++) {
      if (scores[k] > average) {
        overAverageCnt++;
      }
    }
    console.log(((overAverageCnt / N) * 100).toFixed(3) + "%");
  }
}

 

 

Reference link : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

 

반응형