반응형

문제

4 × 3 = 12이다.

이 식을 통해 다음과 같은 사실을 알 수 있다.

3은 12의 약수이고, 12는 3의 배수이다.

4도 12의 약수이고, 12는 4의 배수이다.

두 수가 주어졌을 때, 다음 3가지 중 어떤 관계인지 구하는 프로그램을 작성하시오.

  1. 첫 번째 숫자가 두 번째 숫자의 약수이다.
  2. 첫 번째 숫자가 두 번째 숫자의 배수이다.
  3. 첫 번째 숫자가 두 번째 숫자의 약수와 배수 모두 아니다.

입력

입력은 여러 테스트 케이스로 이루어져 있다. 각 테스트 케이스는 10,000이 넘지않는 두 자연수로 이루어져 있다. 마지막 줄에는 0이 2개 주어진다. 두 수가 같은 경우는 없다.

출력

각 테스트 케이스마다 첫 번째 숫자가 두 번째 숫자의 약수라면 factor를, 배수라면 multiple을, 둘 다 아니라면 neither를 출력한다.

반응형

해설

isFactor 함수와 isMultiple 함수 두가지만 만들면 쉽게 풀 수 있다.

쉬운 문제인 만큼 해설만 있으면 충분할듯

코드

// const dataFile = './data';
const dataFile = '/dev/stdin';
const fs = require('fs');
const inputs = fs.readFileSync(dataFile).toString().split('\n').map(el => el.split(" ").map(el => parseInt(el)));

const run = (inputs) => {
  let i = 0;
  while(true) {
    const input = inputs[i]
    if(isLast(input)) { break; } 
    if(isFactor(input)) {
      console.log("factor")
    } else if (isMultiple(input)) {
      console.log("multiple")
    } else {
      console.log("neither")
    }
    i++;
  }
}

const isLast = (input) => {
  return input[0] == 0 && input[1] == 0;
}

const isFactor = (input) => {
  if(input[0] >= input[1]) {
    return false;
  }

  return input[1]%input[0] == 0
}

const isMultiple = (input) => {
  if(input[1] >= input[0]) {
    return false;
  }

  return input[0]%input[1] == 0
}
run(inputs);
반응형
반응형

정렬 알고리즘 중 삽입 정렬(Insertion sort)에 대해서 설명을 드리고, 백준 알고리즘에서 실전 문제를 푸는 것 까지 진행을 해볼게요.

 

삽입 정렬은 정렬 알고리즘을 처음 접하시는 분들도 매우 직관적으로 이해 할 수 있는 알고리즘 입니다.

 

매우 간단한 알고리즘인데요.

 

간단하게 설명을 드리면

삽입 정렬은 배열의 모든 요소를 하나씩 비교하면서,
비어있는 배열에 하나씩 옮기는 방식의 알고리즘 입니다.
이 옮기는 과정에서 정렬이 되도록 삽입 해줍니다.

즉, 올바른 위치에 계속해서 삽입(Insertion) 만 해준다면 우리는 정렬이 된 배열을 얻을 수 있습니다.

 

그림으로 설명해 드릴게요.

 

저희는 3, 1, 7 3개의 숫자가 있는 배열을 가지고 있고,

이 배열을 정렬을 하고 싶습니다.

오름차순으로 정렬을 하여 1, 3, 7 배열을 얻고 싶은거죠.

 

위에 설명대로 첫번째 3을 비어있는 배열에 옮기겠습니다.

 

첫번째 요소이고 옮길 곳은 배열이 비어있기 때문에 그냥 3을 옮겨놓습니다.

 

다음으로 1을 옮겨야 하는데요.

 

이미 3보다 큰 3이 배열에 들어가 있기 때문에

1은 3보다 앞에 삽입(Insertion) 되어야 합니다.

 

3을 한칸 뒤로 밀고 1을 넣으면 아래처럼 됩니다.

 

마지막으로 7을 넣어야 하는데,

복사본 배열과 비교를 해보니 모든 숫자가 7보다 작네요.

그럼 제일 끝에 삽입을 하면 되겠네요.

 

이렇게 정렬이 완료 되었습니다.

 

백준에서 관련 문제를 찾아보겠습니다.

 

많은 정렬 문제가 있지만, 삽입 정렬은 O(n^2)의 방법이기 때문에 시간제약이 적은 문제를 가서 풀어야합니다.

 

백준에서는 2750번: 수 정렬하기 문제에서 연습해보실 수 있습니다.

 

https://www.acmicpc.net/problem/2750

 

2750번: 수 정렬하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000)이 주어진다. 둘째 줄부터 N개의 줄에는 수 주어진다. 이 수는 절댓값이 1,000보다 작거나 같은 정수이다. 수는 중복되지 않는다.

www.acmicpc.net

 

해설

저는 문제를 풀기에 앞서서 함수를 먼저 작성하였는데요.

 

정렬을 위한 Sort라는 함수를 작성하였고.

 

삽입정렬에서의 삽입 이라는 기능을 위한 insert라는 함수를 정의하였습니다.

 

Sort 함수에서는 기존 배열의 요소들을 하나씩 insert 함수를 사용하여 정렬을 하고,

 

insert 함수는 각 요소가 들어갈 위치를 찾고 삽입하는 역할을 수행합니다.

 

코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
const N = parseInt(input[0]);
 
const list = [];
for(let i = 1; i <= N; i++) {
  list.push(parseInt(input[i]));
}
 
// sort 함수 구현
function sort(unsortedList) {
  const sortedList = []
  for(let num of unsortedList) {
    insert(sortedList, num)
  }
  return sortedList;
}
 
// 삽입 함수 구현
function insert(sortedList, num) {
  let index = -1;
  for(let i = 0; i < sortedList.length; i++) {
    if(sortedList[i] < num) {
      continue;
    } else {
      index = i;
      break;
    }
  }
 
  if(index < 0) {
    sortedList.push(num);
  } else {
    sortedList = sortedList.splice(index, 0, num);
  }
}
 
let sortedList = sort(list);
 
for(let num of sortedList) {
  console.log(num);
}
cs
반응형
반응형

https://www.acmicpc.net/problem/1436

 

1436번: 영화감독 숌

666은 종말을 나타내는 숫자라고 한다. 따라서, 많은 블록버스터 영화에서는 666이 들어간 제목을 많이 사용한다. 영화감독 숌은 세상의 종말 이라는 시리즈 영화의 감독이다. 조지 루카스는 스타

www.acmicpc.net

문제

666은 종말을 나타내는 숫자라고 한다. 따라서, 많은 블록버스터 영화에서는 666이 들어간 제목을 많이 사용한다. 영화감독 숌은 세상의 종말 이라는 시리즈 영화의 감독이다. 조지 루카스는 스타워즈를 만들 때, 스타워즈 1, 스타워즈 2, 스타워즈 3, 스타워즈 4, 스타워즈 5, 스타워즈 6과 같이 이름을 지었고, 피터 잭슨은 반지의 제왕을 만들 때, 반지의 제왕 1, 반지의 제왕 2, 반지의 제왕 3과 같이 영화 제목을 지었다.

하지만 숌은 자신이 조지 루카스와 피터 잭슨을 뛰어넘는다는 것을 보여주기 위해서 영화 제목을 좀 다르게 만들기로 했다.

종말의 숫자란 어떤 수에 6이 적어도 3개이상 연속으로 들어가는 수를 말한다. 제일 작은 종말의 숫자는 666이고, 그 다음으로 큰 수는 1666, 2666, 3666, .... 과 같다.

따라서, 숌은 첫 번째 영화의 제목은 세상의 종말 666, 두 번째 영화의 제목은 세상의 종말 1666 이렇게 이름을 지을 것이다. 일반화해서 생각하면, N번째 영화의 제목은 세상의 종말 (N번째로 작은 종말의 숫자) 와 같다.

숌이 만든 N번째 영화의 제목에 들어간 숫자를 출력하는 프로그램을 작성하시오. 숌은 이 시리즈를 항상 차례대로 만들고, 다른 영화는 만들지 않는다.

입력

첫째 줄에 숫자 N이 주어진다. N은 10,000보다 작거나 같은 자연수이다.

출력

첫째 줄에 N번째 영화의 제목에 들어간 수를 출력한다.

 

해설

브루트포스 문제이기 때문에 "1부터 쭉 진행하며 N번째 숫자를 찾으면 된다" 라는 접근으로 문제를 해결하였다.

 

첫번째로 작성한 코드는 isEndNumber라는 함수로 input 숫자가 종말의 숫자인지(666을 포함하는 숫자인지) boolean의 output을 내는 참수를 작성하였다.

 

해당 함수는 숫자를 스트링으로 변환 후 6이 연속 3번 나오면 true를 반환하도록 하였다.

 

이후 1부터 진행하며 N번대 EndNumber를 찾아 stdout으로 출력하면 끝난다.

 

코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString()
const n = parseInt(input);
 
function isEndNumber(num) {
  const str = String(num);
  let count = 0;
  for(let i = 0; i < str.length; i++) {
    const char = str[i];
    if(char === '6') {
      count++;
    } else {
      count = 0;
    }
 
    if(count >= 3) {
      return true;
    }
  }
 
  return false;
}
 
let currentNum = 1;
let count = 0;
while(true) {
  if(isEndNumber(currentNum)) {
    count++;
    if(count === n) {
      console.log(currentNum);
      break;
    }
  }
  currentNum++;
}
cs
반응형
반응형

https://www.acmicpc.net/problem/1018

 

1018번: 체스판 다시 칠하기

첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

www.acmicpc.net

문제

지민이는 자신의 저택에서 MN개의 단위 정사각형으로 나누어져 있는 M*N 크기의 보드를 찾았다. 어떤 정사각형은 검은색으로 칠해져 있고, 나머지는 흰색으로 칠해져 있다. 지민이는 이 보드를 잘라서 8*8 크기의 체스판으로 만들려고 한다.

체스판은 검은색과 흰색이 번갈아서 칠해져 있어야 한다. 구체적으로, 각 칸이 검은색과 흰색 중 하나로 색칠되어 있고, 변을 공유하는 두 개의 사각형은 다른 색으로 칠해져 있어야 한다. 따라서 이 정의를 따르면 체스판을 색칠하는 경우는 두 가지뿐이다. 하나는 맨 왼쪽 위 칸이 흰색인 경우, 하나는 검은색인 경우이다.

보드가 체스판처럼 칠해져 있다는 보장이 없어서, 지민이는 8*8 크기의 체스판으로 잘라낸 후에 몇 개의 정사각형을 다시 칠해야겠다고 생각했다. 당연히 8*8 크기는 아무데서나 골라도 된다. 지민이가 다시 칠해야 하는 정사각형의 최소 개수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 M이 주어진다. N과 M은 8보다 크거나 같고, 50보다 작거나 같은 자연수이다. 둘째 줄부터 N개의 줄에는 보드의 각 행의 상태가 주어진다. B는 검은색이며, W는 흰색이다.

출력

첫째 줄에 지민이가 다시 칠해야 하는 정사각형 개수의 최솟값을 출력한다.

 

해설

사실 부르트포스에 해설이랄게 뭐 있을까요? ㅠㅠ

하나하나 다 숫자를 세보면 됩니다!

 

저 같은 경우에는 첫번째 칸이 'B' 또는 'W'로 시작하고, 8x8 크기의 체스판을 칠할때

몇번을 칠하면 원하는 색으로 시작하는 체스판이 완성이 되는지

count하는 함수를 작성했어요.

 

countNeedPating(startRowNum, startColNum, startColor) 함수를 작성했고,

인수로 시작하는 row, col, color을 받아서 8x8을 직접 칠해보면서, count를 해줬습니다.

 

여기서 한가지 집중했던 점은

 

8x8 체스판은 64번을 칠하면 완성이 되는데,

첫번째 칸이 'B'일때와 'W'일때의 색칠해야 하는 횟수를 합치면 64가 된다는 것입니다.

따라서 B나 W로 시작하는 경우의 색칠 횟수 한번만 구하면 나머지 경우도 계산이 됩니다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
 
// input을 미리 정리합니다.
const [height, width] = input[0].split(' ').map(el => parseInt(el))
const originBoard = []
for(let i = 1; i <= height; i++) {
  let row = input[i].split('')
  originBoard.push(row)
}
 
// 우리가 색칠 할 Size를 먼저 정의를 해주었습니다.
// 문제를 풀때 x, y 만 수정하면 작은 경우의 수부터 테스트를 진행 할 수 있거든요.
const SIZE = {x:8, y:8}
 
// 색칠 횟수를 count 하는 함수를 먼저 정의합니다.
// 시작하는 위치에서 부터 SIZE에서 정의한 만큼의 체스판을 색칠해봅니다.
function countNeedPainting(startRowNum, startColNum, startColor) {
  let nextColor = startColor;
  let count = 0;
  // i = row
  for(let i = startRowNum; i < startRowNum + SIZE.x; i++) {
    // j = column
    for(let j = startColNum; j < startColNum + SIZE.y; j++) {
      if(originBoard[i][j] !== nextColor) {
        count++;
      }
      nextColor = nextColor === 'B' ? 'W' : 'B'
    }
    nextColor = nextColor === 'B' ? 'W' : 'B'
  }
  return count;
}
 
// 최소값을 미리 넉넉하게 설정해 뒀습니다.
let min = SIZE.x * SIZE.y;
// 전체 크기에서 체스판을 자를 수 있는 모든 경우의 수를 loop를 돌며 확인합니다.
for(let i = 0 ; i <= height - SIZE.x; i++) {
  for(let j = 0; j <= width - SIZE.y; j++) {
    const blakCount = countNeedPainting(i, j, 'B');
    const whiteCount = (SIZE.x * SIZE.y) - blakCount;
    const currentMin = blakCount > whiteCount ? whiteCount : blakCount
 
    if(min > currentMin) {
      min = currentMin
    }
  }
}
 
console.log(min)
cs
반응형
반응형

https://www.acmicpc.net/problem/7568

 

7568번: 덩치

우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩

www.acmicpc.net

문제

우리는 사람의 덩치를 키와 몸무게, 이 두 개의 값으로 표현하여 그 등수를 매겨보려고 한다. 어떤 사람의 몸무게가 x kg이고 키가 y cm라면 이 사람의 덩치는 (x, y)로 표시된다. 두 사람 A 와 B의 덩치가 각각 (x, y), (p, q)라고 할 때 x > p 그리고 y > q 이라면 우리는 A의 덩치가 B의 덩치보다 "더 크다"고 말한다. 예를 들어 어떤 A, B 두 사람의 덩치가 각각 (56, 177), (45, 165) 라고 한다면 A의 덩치가 B보다 큰 셈이 된다. 그런데 서로 다른 덩치끼리 크기를 정할 수 없는 경우도 있다. 예를 들어 두 사람 C와 D의 덩치가 각각 (45, 181), (55, 173)이라면 몸무게는 D가 C보다 더 무겁고, 키는 C가 더 크므로, "덩치"로만 볼 때 C와 D는 누구도 상대방보다 더 크다고 말할 수 없다.

N명의 집단에서 각 사람의 덩치 등수는 자신보다 더 "큰 덩치"의 사람의 수로 정해진다. 만일 자신보다 더 큰 덩치의 사람이 k명이라면 그 사람의 덩치 등수는 k+1이 된다. 이렇게 등수를 결정하면 같은 덩치 등수를 가진 사람은 여러 명도 가능하다. 아래는 5명으로 이루어진 집단에서 각 사람의 덩치와 그 등수가 표시된 표이다.

이름(몸무게, 키)덩치 등수

A (55, 185) 2
B (58, 183) 2
C (88, 186) 1
D (60, 175) 2
E (46, 155) 5

위 표에서 C보다 더 큰 덩치의 사람이 없으므로 C는 1등이 된다. 그리고 A, B, D 각각의 덩치보다 큰 사람은 C뿐이므로 이들은 모두 2등이 된다. 그리고 E보다 큰 덩치는 A, B, C, D 이렇게 4명이므로 E의 덩치는 5등이 된다. 위 경우에 3등과 4등은 존재하지 않는다. 여러분은 학생 N명의 몸무게와 키가 담긴 입력을 읽어서 각 사람의 덩치 등수를 계산하여 출력해야 한다.

입력

첫 줄에는 전체 사람의 수 N이 주어진다. 그리고 이어지는 N개의 줄에는 각 사람의 몸무게와 키를 나타내는 양의 정수 x와 y가 하나의 공백을 두고 각각 나타난다.

출력

여러분은 입력에 나열된 사람의 덩치 등수를 구해서 그 순서대로 첫 줄에 출력해야 한다. 단, 각 덩치 등수는 공백문자로 분리되어야 한다.

 

해설

등수를 구하는 문제이기 때문에 정렬을 하고, 확실하지 않은 경우는 rank를 공동으로 부여하면 된다.

 

하지만 위 같은 방법으로 구현을 할 시에 귀찮다....

 

정렬(nLogn) + rank(n) 으로 구현이 가능하지만 n^2인 방법으로 문제를 풀었다.

 

문제를 푼 접근 방법은

 

"Rank는 결국 나보다 더치가 큰 사람의 숫자 + 1" 이다.

 

나보다 큰 사람이 없으면 rank가 1이 되고, 나보다 큰 사람이 9명이면 내 rank는 10이 된다.

 

다음 코드는 node를 이용하여 구현한 결과이다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
const num = parseInt(input[0]);
 
// Person class를 통해 문제를 풀어보겠습니다.
class Person {
  constructor(weight, height) {
    this.weight = weight;
    this.height = height;
    this.rank = 0;
  }
}
 
const people = []
 
// input 을 통해서 모든 사람의 정보를 people에 저장을 합니다.
for(let i = 1; i <= num; i++) {
  const [weight, height] = input[i].split(" ");
  const person = new Person(weight, height);
  people.push(person);
}
 
// 전체 사람 중에서 '나'보다 덩치가 큰 사람을 count 하고 +1을 하면 내 rank가 됩니다.
for(let i = 0; i < people.length; i++) {
  let currentPeople = people[i];
  const biggerPeople = people.filter(person => person.weight > currentPeople.weight && person.height > currentPeople.height);
  currentPeople.rank = biggerPeople.length + 1;
}
 
// 결과를 출력합니다.
for(let person of people) {
  console.log(person.rank);
}
cs
반응형
반응형

https://www.acmicpc.net/problem/2231

 

2231번: 분해합

어떤 자연수 N이 있을 때, 그 자연수 N의 분해합은 N과 N을 이루는 각 자리수의 합을 의미한다. 어떤 자연수 M의 분해합이 N인 경우, M을 N의 생성자라 한다. 예를 들어, 245의 분해합은 256(=245+2+4+5)이

www.acmicpc.net

문제

어떤 자연수 N이 있을 때, 그 자연수 N의 분해합은 N과 N을 이루는 각 자리수의 합을 의미한다. 어떤 자연수 M의 분해합이 N인 경우, M을 N의 생성자라 한다. 예를 들어, 245의 분해합은 256(=245+2+4+5)이 된다. 따라서 245는 256의 생성자가 된다. 물론, 어떤 자연수의 경우에는 생성자가 없을 수도 있다. 반대로, 생성자가 여러 개인 자연수도 있을 수 있다.

자연수 N이 주어졌을 때, N의 가장 작은 생성자를 구해내는 프로그램을 작성하시오.

입력

첫째 줄에 자연수 N(1 ≤ N ≤ 1,000,000)이 주어진다.

출력

첫째 줄에 답을 출력한다. 생성자가 없는 경우에는 0을 출력한다.

 

해설

문제에서 얻을 수 있는 힌트를 먼저 살펴보면

 

"가장 작은 생성자"

"없을 수도 있다"

 

두가지 정도를 찾을 수 있을것 같다.

 

분해합은 반드시 생성자보다 크기 때문에 0부터 시작하면서 하나씩 생성자가 될 수 있는지 확인을 하면서 진행하면 문제를 풀 수 있다.

 

0부터 시작하는 이유는 "가장 작은 생성자"를 찾는 문제이기 때문이다.

 

만약 "가장 큰 생성자"를 찾는 문제라면 N 부터 0까지 진행하면 된다.

 

쉬운 문제이기 때문에 설명은 코드 주석으로 적어 놓았다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString();
 
const N = parseInt(input)
 
let M = 0
for(let i = 0; i < N; i++) {
  //각 자리수와 후보값의 합을 구하기 위한 변수
  let sum = 0;
 
  // 0부터 시작하는 후보값
  const candidateValue = i;
 
  //각 자리수를 구하기 위해서 숫자를 string으로 변환하여 계산한다.
  const stringValue = candidateValue.toString()
 
  for(let j = 0; j < stringValue.length; j++) {
    sum += parseInt(stringValue[j])
  }
 
  sum += candidateValue;
 
  if(sum == N) {
    M = candidateValue
    break;
  }
}
 
console.log(M)
cs
반응형
반응형

https://www.acmicpc.net/problem/2798

 

2798번: 블랙잭

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는 양의 정수이다. 합이 M을 넘지 않는 카드 3장

www.acmicpc.net

문제

카지노에서 제일 인기 있는 게임 블랙잭의 규칙은 상당히 쉽다. 카드의 합이 21을 넘지 않는 한도 내에서, 카드의 합을 최대한 크게 만드는 게임이다. 블랙잭은 카지노마다 다양한 규정이 있다.

한국 최고의 블랙잭 고수 김정인은 새로운 블랙잭 규칙을 만들어 상근, 창영이와 게임하려고 한다.

김정인 버전의 블랙잭에서 각 카드에는 양의 정수가 쓰여 있다. 그 다음, 딜러는 N장의 카드를 모두 숫자가 보이도록 바닥에 놓는다. 그런 후에 딜러는 숫자 M을 크게 외친다.

이제 플레이어는 제한된 시간 안에 N장의 카드 중에서 3장의 카드를 골라야 한다. 블랙잭 변형 게임이기 때문에, 플레이어가 고른 카드의 합은 M을 넘지 않으면서 M과 최대한 가깝게 만들어야 한다.

N장의 카드에 써져 있는 숫자가 주어졌을 때, M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구해 출력하시오.

입력

첫째 줄에 카드의 개수 N(3 ≤ N ≤ 100)과 M(10 ≤ M ≤ 300,000)이 주어진다. 둘째 줄에는 카드에 쓰여 있는 수가 주어지며, 이 값은 100,000을 넘지 않는 양의 정수이다.

합이 M을 넘지 않는 카드 3장을 찾을 수 있는 경우만 입력으로 주어진다.

출력

첫째 줄에 M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 출력한다.

 

해설

브루트포스(Brute Force) 문제이다.

카드 세장을 뽑아야하기 때문에, 간단하게 3중 loop를 이용해서 문제를 풀수 있다.

다만 한가지 주의를 해야하는 점은, 

! 동일한 카드를 뽑을 수 없다

위 사항만 주의한다면 쉽게 문제를 풀 수 있다.

코드가 간단하기 때문에 자세한 설명은 코드 주석으로 달아놓았다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const fs = require('fs');
const inputs = fs.readFileSync('/dev/stdin').toString().split("\n")
 
const firstLine = inputs[0].split(" ")
const secondLine = inputs[1].split(" ").map(el => parseInt(el))
 
const N = parseInt(firstLine[0])
const M = parseInt(firstLine[1])
 
let max = 0
//3장의 카드를 골라야 하기 때문에 i, j, k 3개의 index를 써서 for loop을 돈다
for(let i = 0; i < N; i++) {
  for(let j = 0; j < N; j++) {
    for(let k = 0; k < N; k++) {
      // 동일한 카드를 선택 할 수는 없기 때문에 아래 조건의 경우 다음 loop로 넘어간다.
      if(i == j || i == k || j == k) {
        continue;
      }
 
      // 서로 다른 카드가 선택된 경우 M을 초과하지 않는 최대값인지 확인 후 저장한다.
      const sum = secondLine[i] + secondLine[j] + secondLine[k]
      if(sum > max && sum <= M) {
        max = sum
      }
 
      // 규칙에 맞는 숫자가 나온 경우에 더이상 계산을 할 필요가 없기 떄문에 종료한다.
      if(max == M) {
        break;
      }
    }
  }
}
 
console.log(max)
 
cs
반응형
반응형

문제

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로 옮기려 한다.

  1. 한 번에 한 개의 원판만을 다른 탑으로 옮길 수 있다.
  2. 쌓아 놓은 원판은 항상 위의 것이 아래의 것보다 작아야 한다.

이 작업을 수행하는데 필요한 이동 순서를 출력하는 프로그램을 작성하라. 단, 이동 횟수는 최소가 되어야 한다.

아래 그림은 원판이 5개인 경우의 예시이다.

입력

첫째 줄에 첫 번째 장대에 쌓인 원판의 개수 N (1 ≤ N ≤ 20)이 주어진다.

 

출력

첫째 줄에 옮긴 횟수 K를 출력한다.

두 번째 줄부터 수행 과정을 출력한다. 두 번째 줄부터 K개의 줄에 걸쳐 두 정수 A B를 빈칸을 사이에 두고 출력하는데, 이는 A번째 탑의 가장 위에 있는 원판을 B번째 탑의 가장 위로 옮긴다는 뜻이다.

 

해설

이 문제는 하오이의 타워 라는 이름으로 유명한 문제이다.

아마 컴퓨터 공학과를 나왔다면, 알고리즘 강의를 듣는 학기초에 한번쯤은 풀어봤을 문제라고 생각한다.

 

재귀로 풀기 위해서는

 

function(N)이 function(N-1)로 점점 작아지면서 Base case까지 작아져야한다.

 

이 문제의 Base Case는 function(1)이 될 것이고, 이 경우 1번에 있는 원판을 3번으로 옮기는 것이 된다.

 

function(2)인 경우를 생각해보면 아래와 같이 진행이 된다.

하오이 타워 원판이 2개일때

 

 

이 문제는 2번 타워를 이용해서 1번에 있는 원판을 3번으로 옮겨라. 라고 표현을 할 수 있다.

 

function moveVia(N, From, To, Via) 와 같은 함수를 만들어서 풀 수 있다.

 

그리고 실제로 원판을 옮기는 함수는

 

function Move(From, To)라는 함수로 정의를 해보자.

 

위에서 N == 1인 경우에는

Move(1, 3)으로 문제가 풀린다.

 

N==2인 경우에는

MoveVia(N:2, From:1, To:3, Via:2)  2개의 원판을 1번에서 3번까지 2번 타워를 이용해서 옮겨라

라는 함수를 실행 할 수 있다.

 

위 그림에서 두번재 단계를 생각해보자

 

 

첫번째로는 원판 1개를 From이 아닌 Via에 가져다 놓아야 한다.

왜냐하면, 더 큰 원판이 아래로 가야하기 때문에 작은 원판을 Via에 가져다 놓는 것이다.

 

즉 MoveVia(N-1, From, Via, To)가 된다. (From에서 Via까지 To를 경유해서 옮겨라.)

 

그 이후에는 Move(From, To)를 이용하여 실제 우리가 옮기는 원판 중 제일 큰 원판을 To로 옮겨 놓는다.

 

마지막으로

Via에 있는 1번 원판을 From을 이용하여 3까지 옮기면 되다.

 

해설에서는 N이 2인 경우만을 생각했지만,

 

N이 커지더라도 동일한 방법이 적용된다.

 

달라지는 점은 MoveVia함수가 하나의 원판을 움직이는데서 끝나지 않고,

여러개의 원판을 재귀를 통해서 계속해서 옮기는 작업을 하는 것만 달라진다.

 

코드로 보면 아래와 같다.

 

글로 설명하기가 어려워, 다른 분들 블로그를 참고했는데

수학이 싫지 않으신 분은 이분의 블로그를 참고해도 좋을 것 같다.

 

https://shoark7.github.io/programming/algorithm/tower-of-hanoi

 

'하노이의 탑' 이해하기

'하노이의 탑' 문제를 이해하고 문제 해결을 위한 핵심 통찰을 살핀 뒤 코드로 작성합니다. 이후 탑의 개수에 따른 총 이동 횟수를 구하는 일반항까지 수학적으로 유도합니다.

shoark7.github.io

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split(' ');
const num = parseInt(input[0]);
 
var towers = [
  [],
  [],
  []
]
var moveCount = 0;
var output = []
 
const hanoi = (num) => {
  // init
  for(let i = 0; i < num; i++) {
    towers[0].push(i)
  }
 
  //num개를 0부터 2까지 옮겨라
  moveVia(num, 021)
 
  console.log(moveCount)
  console.log(output.join('\n'))
}
 
const move = (from, to) => {
  const tmp = towers[from].pop()
  towers[to].push(tmp)
  moveCount++;
  output.push(`${from+1} ${to+1}`)
  return;
}
 
const moveVia = (num, from, to, via) => {
  if(num == 1) {
    return move(from, to)
  }
 
  moveVia(num-1from, via, to);
  move(from, to)
  moveVia(num-1, via, to, from)
}
 
 
hanoi(num)
cs

 

반응형
반응형

문제

재귀적인 패턴으로 별을 찍어 보자. N이 3의 거듭제곱(3, 9, 27, ...)이라고 할 때, 크기 N의 패턴은 N×N 정사각형 모양이다.

크기 3의 패턴은 가운데에 공백이 있고, 가운데를 제외한 모든 칸에 별이 하나씩 있는 패턴이다.

***

* *

***

N이 3보다 클 경우, 크기 N의 패턴은 공백으로 채워진 가운데의 (N/3)×(N/3) 정사각형을 크기 N/3의 패턴으로 둘러싼 형태이다. 예를 들어 크기 27의 패턴은 예제 출력 1과 같다.

입력

첫째 줄에 N이 주어진다. N은 3의 거듭제곱이다. 즉 어떤 정수 k에 대해 N=3k이며, 이때 1 ≤ k < 8이다.

출력

첫째 줄부터 N번째 줄까지 별을 출력한다.

 

해설

이 문제는 그림을 그리면서 먼저 패턴을 파악하면 쉽게 풀 수 있을 것 같다.

입력값이 3 일때를 생각해보면

x x x
x   x
x x x

인데, 이 경우에 1,1이 공백을 출력해야 하는 위치인 것을 알 수 있고,

이 패턴이 반복되기 때문에 i, j의 1 % 3(1 나머지 3)의 값이 1인 곳은 공백으로 처리를 하면 첫번째 단계는 해결이 된다.

이 경우가 재귀에서 처리해는 최소범위의 값이다.

 

이 코드를 그래도 9로 넘어갈 경우

x x x x x x x x x
x   x x   x x   x
x x x x x x x x x
x x x x x x x x x
x   x x   x x   x
x x x x x x x x x
x x x x x x x x x
x   x x   x x   x
x x x x x x x x x

색칠된 부분이 정답과는 다른 부분이 된다.

즉, 첫번째 우리가 공백으로 처리한 부분 이외에 추가로 공백으로 처리해야하는 좌표는 아래와 같다.(좌표를 편의상 i, j로 작성합니다. for loop에서 자주 사용되는 변수입니다.)

 

(3,3) (3,4) (3,5)

(4,3) (4,4) (4,5)

(5,4) (5,4) (5,5)

 

여기서 i, j 의 범위는 모두

3^1 <= i,j < 3^1 + 3^1 이다.(^1은 1승, 제곱인 경우 ^2로 표현합니다.)

 

즉, 3^1부터 3^1 크기의 정 사각형이 빈 공백으로 표시가 된다.

 

입력값이 27이라면

3^2 부터 3^2+3^2까지 (9 ~ 17) 공백으로 표현이 된다.

 

이 규칙에서 나온 코드가 아래 코드에서 30번째 라인의 코드이다.

3~5까지는 모두 공백(n%3==1일때)으로 출력이 되도록 3으로 나눈 값의 몫만을 취해서 재귀로 호출한다.

그렇다면 9,9는 3,3이 되고, 다시 1,1이 되면서 빈 공백을 출력하게 된다.

나머지는 제외한 몫만을 취급하기 때문에

(17,17, 27) => (5,5,9) => (1, 1, 3)이 되면서 num이 1이 되기전에 line 20에서 분기에 걸려 " " 빈 공백을 출력하게 된다.

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
const num = parseInt(input[0])
 
const lines = []
 
printStars(num);
console.log(lines.join(""))
 
function printStars(num) {
  for(let i = 0; i < num; i++) {
    for(let j = 0; j < num; j++) {
      star(i, j, num)
    }
    lines.push("\n")
  }
}
 
function star(i, j, num) {
  if(i % 3 == 1 && j % 3 == 1) {
    // (1,1), (1,4), (1,7)...
    lines.push(" ")
  } else {
    if(num == 1) {
      lines.push("*")
    } else {
      // (3,3) = (1,1)이 되고,
      // (3,4) = (1,1)이 된다.
      // (9,9), (27,27)도 동일한 방식으로 재귀 호출된다.
      star(parseInt(i/3), parseInt(j/3), parseInt(num/3))
    }
  }
}
cs

 

반응형
반응형

문제

피보나치 수는 0과 1로 시작한다. 0번째 피보나치 수는 0이고, 1번째 피보나치 수는 1이다. 그 다음 2번째 부터는 바로 앞 두 피보나치 수의 합이 된다.

이를 식으로 써보면 Fn = Fn-1 + Fn-2 (n ≥ 2)가 된다.

n=17일때 까지 피보나치 수를 써보면 다음과 같다.

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597

n이 주어졌을 때, n번째 피보나치 수를 구하는 프로그램을 작성하시오.

입력

첫째 줄에 n이 주어진다. n은 20보다 작거나 같은 자연수 또는 0이다.

출력

첫째 줄에 n번째 피보나치 수를 출력한다.

 

해설

피보나치 수를 구하는 함수를 작성하기 위해서는

최소케이스를 먼저 작성을 해야한다.

 

이 문제에서의 최소 케이스는 fibonacci(0) 은 0, fibonacci(1)은 1 을 리턴하도록 작성을 한다.

이후 2 부터는 는 fibonacci(n-1) + fibonacci(n-1)를 재귀를 통해 구해서 더하도록 작성하면 된다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require('fs');
const input = fs.readFileSync('/dev/stdin').toString().split('\n');
 
const num = input[0]
 
console.log(fibonacci(num))
 
function fibonacci(n) {
  if(n == 0) {
    return 0
  } else if(n == 1) {
    return 1
  }
 
  return fibonacci(n-1+ fibonacci(n-2)
}
cs
반응형

+ Recent posts