본문 바로가기
Algorithm/BOJ

for문 편(2) - Node.js로 [백준/BOJ] 단계별로 풀어보기를 풀어보다

by Muko 2020. 4. 17.

BOJ 단계별로 풀어보기 'for문' - 2편

이번 포스팅에서는 저번에 작성한 BOJ 단계별로 풀어보기 - for문 1편에 이어서 2편을 작성해보려고 합니다. 저번 시간에 '기찍 N' 문제까지 풀었었고, 이번 포스팅에서는 'A+B - 7'부터 'X보다 작은 수'문제까지 쭉 풀어보도록 하겠습니다.

7. BOJ 11021 - A+B - 7

이 문제는 두 개의 정수 A와 B를 입력받았을 때 A+B를 출력하는 문제입니다. 매우 쉬워보이죠? 이 문제를 푸는 핵심 코드는 결국 입력 받아서 A+B 연산 결과를 출력해야 하는 BOJ A+B문제 와 비슷하게 해결할 수 있습니다. 그런데 백준에 동일한 문제가 두 개 있을 이유는 없겠죠? 여기서의 차이점은 바로 테스트 케이스의 존재입니다. 기존의 A+B 문제는 입력으로부터 정수 두 개를 받아서 결과를 출력했다면, 이 문제에서는 이러한 동작을 테스트 케이스의 수 만큼 동작해야 합니다. 당연히 같은 동작, 즉 연산을 반복해야하므로 반복문을 사용하면 쉽게 문제를 풀 수 있습니다.

코드 접기/펼치기
const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const input = [];
rl.on('line', line => {
  input.push(line.split(' '));
}).on('close', () => {
  const testcase = parseInt(input[0]);

  let result = '';
  for(let t=1; t<=testcase; t++){
    const A = parseInt(input[t][0]);
    const B = parseInt(input[t][1]);

    result += `Case #${t}: ${A+B}\n`;
  }
  console.log(result);
  process.exit();
});

 

여기서 result += `Case #${t}: ${A+B}\n` 이 부분이 이해가 되지 않는 분들이 있으실 겁니다. 저도 몰랐다가 얼마 전에 알았거든요! 이렇게 작성하는 문법을 백틱을 이용한 템플릿 리터럴 이라고 합니다. 조금 더 자세하게 살펴보고 싶은 분들은 링크를 들어가셔서 포스팅을 보고 오시면 좋을 것 같습니다. 다른 참고 자료와는 다르게 내용이 짧으니까 금방 볼 수 있어요!

 

8. BOJ 11022 = A+B - 8

이 문제는 출력 형식만 다를 뿐 똑같습니다. 출력에서 어떤 값을 사용한 연산인지 식까지 출력하라는 문제이기 때문에, 방금 전에 해결했던 코드를 살포시 가져와서 조금만 수정하면 풀 수 있는 문제입니다.

코드 접기/펼치기
const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const input = [];
rl.on('line', line => {
  input.push(line.split(' '));
}).on('close', () => {
  const testcase = parseInt(input[0]);

  let result = '';
  for(let t=1; t<=testcase; t++){
    const A = parseInt(input[t][0]);
    const B = parseInt(input[t][1]);

    result += `Case #${t}: ${A} + ${B} = ${A+B}\n`;
  }
  console.log(result);
  process.exit();
});

 

9. BOJ 2438 - 별 찍기 - 1

드디어 별 찍기 문제가 나왔습니다. 프로그래밍 언어를 처음 익히면서 반복문을 익숙하게 다루기 위해서는 거쳐가야할 필수 코스입니다. 위의 문제들은 반복문을 어떻게 사용하는지 문법 자체를 눈에 익히는데 사용되는 문제라면, 이 문제부터는 실질적으로 반복문 특히 for문을 자유자재로 다룰 수 있도록 해주는 아주 좋은 예제들입니다. 말 그대로 프로그래밍 숙련도 쌓기용 문제입니다.

이 문제는 입력으로 정수 N을 받고나서 첫 번째 줄부터 N번 째 줄까지 차례대로 그 줄의 번호만큼 별의 갯수를 출력해야 합니다. 이 문제는 특히 바로 아래 코드를 펼쳐보지 마시고, 어떻게든 혼자서 해결해보셨으면 좋겠습니다. 어렵고 고통스럽고 맞왜틀 (맞는데 왜 틀렸지?) 를 계속해서 겪어야 프로그래머로서 기초 소양인 알고리즘에 대한 소양을 갈고 닦을 수 있습니다. 모두 파이팅입니다!

코드 접기/펼치기
const readline = require('readline');
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.on('line', line => {
  const N = parseInt(line);

  let result = '';
  for(let row=0; row<N; row++){
    let stars = '';
    for(let col=0; col<=row; col++){
      stars += '*';
    }
    result += stars + '\n';
  }
  console.log(result);
}).on('close', () => {
  process.exit();
});

 

위에 처럼 작성해도 무난하게 정답이라는 결과 화면을 받을 수 있지만, 우리는 아주 조금만 더 보기 좋게 코드를 작성해봅시다. 백준 사이트는 어떤 문제를 풀고나면 다른 사람들이 공개한 코드들을 볼 수 있는데요, 여기서 초심자 분들이 조심하셔야 할 부분이 있습니다. 바로 '실행 시간'에 너무 초점을 두면 안된다는 것입니다. '아니 프로그램이 조금이라도 더 빠르게 짜는게 개발자가 해야하는거 아냐? 이게 무슨 말이지?'라고 의문이 생길 수 있습니다. 제가 드리고 싶은 말은 실행 시간에만 초점을 두고 코드를 짜는 습관을 들이면, 특히 백준에 널려있는 상위 랭크 풀이법들을 참고해서 코드를 짜다보면 '읽기 어려운 코드'를 짜는 사람이 될 가능성이 매우 높아집니다. 우리가 알고리즘을 통해 프로그래밍을 공부하는 이유는 효율적인 코드를 짜는 습관을 들이고 문제 해결 능력을 키우기 위한 연습이라는 목적도 있지만, 다른 사람이 읽기 쉬운 코드를 짜는 연습이라는 점도 잊어서는 안됩니다.

어떤 회사에서 10만 줄 짜리 코드를 작성해서 힘들게 프로젝트를 완료했다고 생각해보세요. 그리고 그 프로젝트의 핵심을 맡아서 개발하던 사람이 다른 회사로 이직하게 되었습니다. 물론 그 과정에서 다른 사람에게 '이 프로젝트에는 어떤 기능들이 있습니다'라고 인수인계를 하고 떠났겠죠. 이제 이직한 사람이 하던 일을 맡아서 하게된 새로운 직원은 멘붕에 빠지게 됩니다. 3만 줄이나 되는 코드를 읽고 이해하는데만 최소 한 달이 걸릴 것 같았기 때문입니다. 작성한 사람이 속한 팀이 이 주일 만에 작성했다고 가정한다면, 해당 코드를 이해하는데만 최소 두 배의 시간이 걸린다는 의미입니다.

여기서 주목해야할 점이 바로 '읽는 사람'입니다. 프로그램은 만들어지는 시간보다 사용되는 시간이 훨씬 길고, 자연스레 유지보수라는 작업이 가지는 시간이 전체 프로젝트에서 차지하는 비중은 압도적일 것입니다. 그래서 처음부터 코드를 작성할 때 간결하게 작성하는 습관이 필요한거죠. 나중에 코드를 읽을 자신과 다른 사람을 위해서 말이죠.

그런 의미에서 제가 위에 적은 풀이 방법을 아주 조금 분리시켜 봅시다. 분리시키면 분리시킬 수록 나중에 유지보수가 훨씬 용이하거든요!

코드 접기/펼치기
// 입출력에 사용할 rl을 받아오는 함수
const getRl = () => {
  const readline = require('readline');
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
}
const rl = getRl();

// N을 입력으로 받았을 때 별찍기 문자열을 반환하는 함수
const printStars = (N) => {
  let result = '';
  for(let row=0; row<N; row++){
    let stars = '';
    for(let col=0; col<=row; col++){
      stars += '*';
    }
    result += stars + '\n';
  }
  console.log(result);
}

// 입력 받아와서 알고리즘 동작하는 함수
const start = (rl) => {
  rl.on('line', line => {
    const N = parseInt(line);
    printStars(N);
  }).on('close', () => {
    process.exit();
  });
}

// 프로그램 동작
start(rl);

 

모든 기능을 함수로 만들어서 잘게 나누었습니다. 이렇게 작성하면 나중에 에러가 발생했을 때 어디서 문제가 발생했는지 발견하기가 훨씬 용이합니다. 또한 다른 사람이 읽기 편하게끔 주석을 달아놓았습니다. 훨씬 깔끔하죠? 만약 아니라면, 제가 더 열심히 공부하고 연습해서 좋은 코드로 수정하겠습니다. 흑흑...

 

10. BOJ 2439 - 별 찍기 - 2

이번에도 별 찍기 문제입니다. 차이점은 별들이 오른쪽으로 정렬되어 있다는 것입니다. 따라서 공백과 별의 갯수를 적절하게 조절해가면서 반복문을 코드로 작성하면 풀 수 있는 문제입니다. 프로그래밍이 처음이신 분들은 정말 머리를 싸매야 풀 수 있으실 겁니다. 그만큼 익숙해지기 위한 성장통이라 생각하시고 차근차근 풀어보세요! 핵심은 포문에 사용될 변수들을 어떻게 초기화하고, 제한 조건을 무엇으로 둘 것이며, 그 안에서 어떤 코드가 동작되어야 하는지를 차분히 정리해야 코드를 작성할 수 있습니다. 아래 코드는 정말 고민하다가 안 풀릴 때 참고해주세요!

코드 접기/펼치기
// 입출력에 사용할 rl을 받아오는 함수
const getRl = () => {
  const readline = require('readline');
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
}
const rl = getRl();

// N을 입력으로 받았을 때 별찍기 문자열을 반환하는 함수
const printStars = (N) => {
  let result = '';
  for(let row=0; row<N; row++){
    let blanks = ''; 
    let stars = '';

    // 공백
    for(let col=0; col<N-row-1; col++){
      blanks += ' ';
    }

    // 별
    for(let col=0; col<=row; col++){
      stars += '*';
    }
    result += blanks + stars + '\n';
  }
  console.log(result);
}

// 입력 받아와서 알고리즘 동작하는 함수
const start = (rl) => {
  rl.on('line', line => {
    const N = parseInt(line);
    printStars(N);
  }).on('close', () => {
    process.exit();
  });
}

// 프로그램 동작
start(rl);

 

11. BOJ 10871 - X보다 작은 수

이 문제는 지금까지 풀었던 문제 중에서 입력 개수가 가장 많은 문제입니다. 심지어 두 줄로 이루어져 있습니다.

첫째 줄에는 정수 N과 X가 주어지고, 둘째 줄에는 수열 A를 이루는 정수 N개가 주어집니다. 주어지는 정수는 모두 1이상 1만 이하입니다. 이 때 우리가 구현해야하는 것은 주어진 수열에서 X보다 작은 수를 입력받은 순서대로 공백으로 구분해서 출력하는 것입니다. 문제의 조건에서 X보다 작은 수는 적어도 하나 존재한다고 하니 정답이 없는 경우는 없다는 것을 전제로 하고 있음을 파악할 수 있습니다.

이 문제는 반복문을 돌면서 문제에서 제시하는 X보다 작은 수를 체크하는 부분을 구현하고, 그 값들을 저장할 수 있어야 정답을 받을 수 있습니다. 계속해서 고민하다보면 어떻게 구현할 수 있을지 알 수 있습니다. 조금만 더 고민해보세요! 생각보다 코드 자체는 간단합니다.

코드 접기/펼치기
// 입출력에 사용할 rl을 받아오는 함수
const getRl = () => {
  const readline = require('readline');
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout
  });
}
const rl = getRl();

// 주어진 입력들을 이용해서 정답을 반환하는 함수
const getAnswers = (N, X, numbers) => {
  const answers = [];

  for(let i=0; i<N; i++){
    const number = parseInt(numbers[i]);

    if(number < X){
      answers.push(number);
    }
  }

  return answers.join(' ');
};

// 입력 받아와서 알고리즘 동작하는 함수
const input = [];
const start = (rl) => {
  rl.on('line', line => {
    input.push(line.split(' '));
  }).on('close', () => {
    const N = parseInt(input[0][0]);
    const X = parseInt(input[0][1]);
    const numbers = input[1];

    console.log(getAnswers(N, X, numbers));
    process.exit();
  });
}

// 프로그램 동작
start(rl);

 

물론 다른 방식으로 정답을 문자열로 출력할 수 있지만, 제가 작성한 코드에서 정보를 가지고 있는 자료형식이 배열이라는 점을 이용해서 배열의 메서드를 사용해서 정답을 반환하도록 구현했습니다. 배열에는 join이라는 메서드가 있는데요, 이는 각 배열의 요소들을 join메서드 안의 파라미터로 넣은 값을 사이사이에 넣어서 문자열 하나로 반환하게 해주는 기능을 가지고 있습니다. 만약 배열에 [1, 2, 3]이라고 가지고 있을 때 [1, 2, 3].join('--'); 이라고 코드를 실행하면 1--2--3이라는 결과를 받을 수 있습니다.

이번 for문 편을 참 길었죠? 여기까지 따라오신 모든 분들은 정말 대단하신 분들입니다. 포기하지 않고 따라오셨으니까요!!
혹시 읽으면서 이해가 되지 않는 부분은 언제든지 댓글로 남겨주시면 빠르게 답변 남기겠습니다.

모두 수고하셨습니다 👍

댓글7