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

 

3986번: 좋은 단어

이번 계절학기에 심리학 개론을 수강 중인 평석이는 오늘 자정까지 보고서를 제출해야 한다. 보고서 작성이 너무 지루했던 평석이는 노트북에 엎드려서 꾸벅꾸벅 졸다가 제출 마감 1시간 전에

www.acmicpc.net

문제

이번 계절학기에 심리학 개론을 수강 중인 평석이는 오늘 자정까지 보고서를 제출해야 한다. 보고서 작성이 너무 지루했던 평석이는 노트북에 엎드려서 꾸벅꾸벅 졸다가 제출 마감 1시간 전에 깨고 말았다. 안타깝게도 자는 동안 키보드가 잘못 눌려서 보고서의 모든 글자가 A와 B로 바뀌어 버렸다! 그래서 평석이는 보고서 작성을 때려치우고 보고서에서 '좋은 단어'나 세보기로 마음 먹었다.

평석이는 단어 위로 아치형 곡선을 그어 같은 글자끼리(A는 A끼리, B는 B끼리) 쌍을 짓기로 하였다. 만약 선끼리 교차하지 않으면서 각 글자를 정확히 한 개의 다른 위치에 있는 같은 글자와 짝 지을수 있다면, 그 단어는 '좋은 단어'이다. 평석이가 '좋은 단어' 개수를 세는 것을 도와주자.

입력

첫째 줄에 단어의 수 N이 주어진다. (1 ≤ N ≤ 100)

다음 N개 줄에는 A와 B로만 이루어진 단어가 한 줄에 하나씩 주어진다. 단어의 길이는 2와 100,000사이이며, 모든 단어 길이의 합은 1,000,000을 넘지 않는다.

출력

첫째 줄에 좋은 단어의 수를 출력한다.

예제 입력 1 복사

3
ABAB
AABB
ABBA

예제 출력 1 복사

2

예제 입력 2 복사

3
AAA
AA
AB

예제 출력 2 복사

1

 

문제풀이(1)

꾸벅꾸벅 졸다가 모든 글자의 A와 B가 바뀌었다면 그냥 ctr+z를 눌르면 될 것 같다는 생각이 들었지만 예시이기 때문에 넘어가기로 했다.
단어 위로 아치형 곡선을 그어 같은 글자끼리 짝을 짓고 선이 겹치지 않는 다는 것이 어떤 말인지 잘 감이 잡히지 않아서 직접 그려보았다.


ABAB -> 곡선을 그었을때 첫번째 B와 두번쨰 A의 곡선이 겹치기 떄문에 좋은 단어가 아니다.
AABB -> 곡선을 그었을때 겹치지 않기 떄문에 좋은 단어이다.
ABBA -> 곡선을 그었을때 겹치지 않기 때문에 좋은 단어이다.
규칙성을 보았을 때, 올바른 괄호와 같은 형식으로 풀면될 것 같다.

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

let answer = 0;

arr.forEach((word) => {
  if (word.length % 2 === 0) {
    const stack = [];

    for (let i = 0; i < word.length; i++) {
      if (stack.length === 0) {
        stack.push(word[i]);
        continue;
      }

      if (stack.at(-1) === word[i]) {
        stack.pop();
      } else {
        stack.push(word[i]);
      }
    }

    if (stack.length === 0) {
      answer++;
    }
  }
});

console.log(answer);

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

 

11652번: 카드

준규는 숫자 카드 N장을 가지고 있다. 숫자 카드에는 정수가 하나 적혀있는데, 적혀있는 수는 -262보다 크거나 같고, 262보다 작거나 같다. 준규가 가지고 있는 카드가 주어졌을 때, 가장 많이 가지

www.acmicpc.net

문제

준규는 숫자 카드 N장을 가지고 있다. 숫자 카드에는 정수가 하나 적혀있는데, 적혀있는 수는 -2^62보다 크거나 같고, 2^62보다 작거나 같다.

준규가 가지고 있는 카드가 주어졌을 때, 가장 많이 가지고 있는 정수를 구하는 프로그램을 작성하시오. 만약, 가장 많이 가지고 있는 정수가 여러 가지라면, 작은 것을 출력한다.

입력

첫째 줄에 준규가 가지고 있는 숫자 카드의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개 줄에는 숫자 카드에 적혀있는 정수가 주어진다.

출력

첫째 줄에 준규가 가장 많이 가지고 있는 정수를 출력한다.

예제 입력 1 복사

5
1
2
1
2
1

예제 출력 1 복사

1

예제 입력 2 복사

6
1
2
1
2
1
2

예제 출력 2 복사

1

 

문제풀이(1)

가지고 있는 정수 중에 가장 많이 가지고 있는 정수를 고르되, 가장 많이 가지고 있는 정수가 여러개라면 낮은 수를 출력한다.
Map을 사용하여 카운팅하면서 동시에 가장 큰수와, 가장 큰수의 개수를 비교하여 변경한다.
Number 범위를 초과하기 때문에 BigInt를 사용해야하며 출력시 toString을 사용해야한다.

const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
const [N, ...arr] = require("fs").readFileSync(filePath).toString().trim().split("\n").map(BigInt);

const map = new Map();
let maxNum;
let maxCount = 0;

for (let i = 0; i < N; i++) {
  const num = arr[i];

  map.has(num) ? map.set(num, map.get(num) + 1) : map.set(num, 1);

  if (num === maxNum) {
    maxCount++;
    continue;
  }

  if (map.get(num) > maxCount) {
    maxNum = num;
    maxCount = map.get(num);
  } else if (map.get(num) === maxCount && num < maxNum) {
    maxNum = num;
    maxCount = map.get(num);
  }
}

console.log(maxNum.toString());

 

문제풀이(2) - 리팩토링

1. 코드의 가독성 향상 - 반복적으로 사용되는 값 변수할당, 조건문 간소화

const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
const [N, ...arr] = require("fs").readFileSync(filePath).toString().trim().split("\n").map(BigInt);

const map = new Map();
let maxNum;
let maxCount = 0;

for (let i = 0; i < N; i++) {
  const num = arr[i];
  const currentCount = (map.get(num) || 0) + 1;

  map.set(num, currentCount);

  if (num === maxNum) {
    maxCount++;
  } else if (currentCount > maxCount || (currentCount === maxCount && num < maxNum)) {
    maxNum = num;
    maxCount = currentCount;
  }
}

console.log(maxNum.toString());

 

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

 

9372번: 상근이의 여행

첫 번째 줄에는 테스트 케이스의 수 T(T ≤ 100)가 주어지고, 각 테스트 케이스마다 다음과 같은 정보가 주어진다. 첫 번째 줄에는 국가의 수 N(2 ≤ N ≤ 1 000)과 비행기의 종류 M(1 ≤ M ≤ 10 000) 가

www.acmicpc.net

문제

상근이는 겨울방학을 맞아 N개국을 여행하면서 자아를 찾기로 마음먹었다. 

하지만 상근이는 새로운 비행기를 무서워하기 때문에, 최대한 적은 종류의 비행기를 타고 국가들을 이동하려고 한다.

이번 방학 동안의 비행 스케줄이 주어졌을 때, 상근이가 가장 적은 종류의 비행기를 타고 모든 국가들을 여행할 수 있도록 도와주자.

상근이가 한 국가에서 다른 국가로 이동할 때 다른 국가를 거쳐 가도(심지어 이미 방문한 국가라도) 된다.

입력

첫 번째 줄에는 테스트 케이스의 수 T(T ≤ 100)가 주어지고,

각 테스트 케이스마다 다음과 같은 정보가 주어진다.

  • 첫 번째 줄에는 국가의 수 N(2 ≤ N ≤ 1 000)과 비행기의 종류 M(1 ≤ M ≤ 10 000) 가 주어진다.
  • 이후 M개의 줄에 a와 b 쌍들이 입력된다. a와 b를 왕복하는 비행기가 있다는 것을 의미한다. (1 ≤ a, b ≤ n; a ≠ b) 
  • 주어지는 비행 스케줄은 항상 연결 그래프를 이룬다.

출력

테스트 케이스마다 한 줄을 출력한다.

  • 상근이가 모든 국가를 여행하기 위해 타야 하는 비행기 종류의 최소 개수를 출력한다.

예제 입력 1 복사

2
3 3
1 2
2 3
1 3
5 4
2 1
2 3
4 3
4 5

예제 출력 1 복사

2
4

 

문제풀이(1)

상근이가 가장 적은 종류의 비행기를 타고 이동하는 값을 찾는 문제이다. (다른 국가를 거쳐가도 된다.)
첫번째는 테스트 케이스의 수, 두번째부터는 국가의 수와 비행기의 종류가 입력된다.
모든 곳이 연결된 비행 스케줄은 항상 연결 그래프를 이루기 때문에 (나라수 - 1)이 된다.

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

const answer = [];

for (let i = 0; i < T; i++) {
  const [country, airplane] = arr[0].split(" ").map(Number);
  arr.shift();
  for (let j = 0; j < airplane; j++) {
    arr.shift();
  }
  answer.push(country - 1);
}

console.log(answer.join("\n"));

 

문제풀이(2)

문제풀이(1)와 같이 풀 경우 shift를 많이 사용하여 배열의 이동이 잦아지기 때문에 비효율적이다.
1. shift메소드를 사용하지 않고, 인덱스를 직접관리해서 효율적으로 변경한다.
시간복잡도 - O(n * m) -> O(n)
메모리 - 31944kb -> 20168ms
실행시간 - 4272kb -> 160ms

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

let index = 0;
const T = Number(input[index++]);

let result = "";

for (let i = 0; i < T; i++) {
  const [country, airplane] = input[index++].split(" ").map(Number);
  index += airplane;
  result += country - 1 + "\n";
}

console.log(result.trim());

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

 

1120번: 문자열

길이가 N으로 같은 문자열 X와 Y가 있을 때, 두 문자열 X와 Y의 차이는 X[i] ≠ Y[i]인 i의 개수이다. 예를 들어, X=”jimin”, Y=”minji”이면, 둘의 차이는 4이다. 두 문자열 A와 B가 주어진다. 이때, A의

www.acmicpc.net

문제

길이가 N으로 같은 문자열 X와 Y가 있을 때, 두 문자열 X와 Y의 차이는 X[i] ≠ Y[i]인 i의 개수이다. 예를 들어, X=”jimin”, Y=”minji”이면, 둘의 차이는 4이다.

두 문자열 A와 B가 주어진다. 이때, A의 길이는 B의 길이보다 작거나 같다. 이제 A의 길이가 B의 길이와 같아질 때 까지 다음과 같은 연산을 할 수 있다.

  1. A의 앞에 아무 알파벳이나 추가한다.
  2. A의 뒤에 아무 알파벳이나 추가한다.

이때, A와 B의 길이가 같으면서, A와 B의 차이를 최소로 하는 프로그램을 작성하시오.

입력

첫째 줄에 A와 B가 주어진다. A와 B의 길이는 최대 50이고, A의 길이는 B의 길이보다 작거나 같고, 알파벳 소문자로만 이루어져 있다.

출력

A와 B의 길이가 같으면서, A와 B의 차이를 최소가 되도록 했을 때, 그 차이를 출력하시오.

예제 입력 1 복사

adaabc aababbc

예제 출력 1 복사

2

예제 입력 2 복사

hello xello

예제 출력 2 복사

1

 

문제풀이(1)

문자열 A를 B에 겹쳤을 때 다른곳의 최소값을 구하면된다.
A문자열의 길이 - B의 문자열의 길이만큼 반복순회하고,
B의 문자열을 한칸씩 뒤로 미루면서 다른곳을 count하여 가장 작은 값을 찾아낸다. -> 슬라이딩 윈도우 기법

const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
const [A, B] = require("fs").readFileSync(filePath).toString().trim().split(" ");

let min = Infinity;

// A와 B의 차이만큼 반복한다.
for (let i = 0; i <= B.length - A.length; i++) {
  let diff = 0;
  // 한칸씩 뒤로 미루면서 가장 차이가 적은 것을 골라낸다.
  for (let j = 0; j < A.length; j++) {
    if (A[j] !== B[i + j]) {
      diff++;
    }
  }
  min = Math.min(min, diff);
}

console.log(min);

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

 

1057번: 토너먼트

김지민은 N명이 참가하는 스타 토너먼트에 진출했다. 토너먼트는 다음과 같이 진행된다. 일단 N명의 참가자는 번호가 1번부터 N번까지 배정받는다. 그러고 난 후에 서로 인접한 번호끼리 스타를

www.acmicpc.net

문제

김지민은 N명이 참가하는 스타 토너먼트에 진출했다. 토너먼트는 다음과 같이 진행된다. 일단 N명의 참가자는 번호가 1번부터 N번까지 배정받는다. 그러고 난 후에 서로 인접한 번호끼리 스타를 한다. 이긴 사람은 다음 라운드에 진출하고, 진 사람은 그 라운드에서 떨어진다. 만약 그 라운드의 참가자가 홀수명이라면, 마지막 번호를 가진 참가자는 다음 라운드로 자동 진출한다. 다음 라운드에선 다시 참가자의 번호를 1번부터 매긴다. 이때, 번호를 매기는 순서는 처음 번호의 순서를 유지하면서 1번부터 매긴다. 이 말은 1번과 2번이 스타를 해서 1번이 진출하고, 3번과 4번이 스타를 해서 4번이 진출했다면, 4번은 다음 라운드에서 번호 2번을 배정받는다. 번호를 다시 배정받은 후에 한 명만 남을 때까지 라운드를 계속 한다.

마침 이 스타 대회에 임한수도 참가했다. 김지민은 갑자기 스타 대회에서 우승하는 욕심은 없어지고, 몇 라운드에서 임한수와 대결하는지 궁금해졌다. 일단 김지민과 임한수는 서로 대결하기 전까지 항상 이긴다고 가정한다. 1 라운드에서 김지민의 번호와 임한수의 번호가 주어질 때, 과연 김지민과 임한수가 몇 라운드에서 대결하는지 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 참가자의 수 N과 1 라운드에서 김지민의 번호와 임한수의 번호가 순서대로 주어진다. N은 2보다 크거나 같고, 100,000보다 작거나 같은 자연수이고, 김지민의 번호와 임한수의 번호는 N보다 작거나 같은 자연수이고, 서로 다르다.

출력

첫째 줄에 김지민과 임한수가 대결하는 라운드 번호를 출력한다. 만약 서로 대결하지 않을 때는 -1을 출력한다.

예제 입력 1 복사

16 1 2

예제 출력 1 복사

1

예제 입력 2 복사

16 8 9

예제 출력 2 복사

4

예제 입력 3 복사

1000 20 31

예제 출력 3 복사

4

 

문제풀이(1)

N명의 참가자수 중에서 김지민과 임한수가 무조건 이겨서 둘이 대결하는 라운드를 찾는 문제이다.
이진탐색 알고리즘을 사용하여 풀수 있다.
김지민과 임한수가 무조건 이겨서 올라가고 2명식 경기를 치루기 때문에 각자의 번호 / 2를 해서 반복해서 올라가면서 만나기 전까지를 카운트하면된다.
이문제와 비슷한 문제는 프로그래머스에서 푼기억이 있어서 쉽게 풀 수 있었다.

const filePath = process.platform === "linux" ? "/dev/stdin" : "./input.txt";
let [N, Kim, Lim] = require("fs").readFileSync(filePath).toString().trim().split(" ").map(Number);

let count = 0;

while (Kim !== Lim) {
  Kim = Math.ceil(Kim / 2);
  Lim = Math.ceil(Lim / 2);
  count++;
}

console.log(count);

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

 

1049번: 기타줄

첫째 줄에 N과 M이 주어진다. N은 100보다 작거나 같은 자연수이고, M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 M개의 줄에는 각 브랜드의 패키지 가격과 낱개의 가격이 공백으로 구분하여 주

www.acmicpc.net

문제

Day Of Mourning의 기타리스트 강토가 사용하는 기타에서 N개의 줄이 끊어졌다. 따라서 새로운 줄을 사거나 교체해야 한다. 강토는 되도록이면 돈을 적게 쓰려고 한다. 6줄 패키지를 살 수도 있고, 1개 또는 그 이상의 줄을 낱개로 살 수도 있다.

끊어진 기타줄의 개수 N과 기타줄 브랜드 M개가 주어지고, 각각의 브랜드에서 파는 기타줄 6개가 들어있는 패키지의 가격, 낱개로 살 때의 가격이 주어질 때, 적어도 N개를 사기 위해 필요한 돈의 수를 최소로 하는 프로그램을 작성하시오.

입력

첫째 줄에 N과 M이 주어진다. N은 100보다 작거나 같은 자연수이고, M은 50보다 작거나 같은 자연수이다. 둘째 줄부터 M개의 줄에는 각 브랜드의 패키지 가격과 낱개의 가격이 공백으로 구분하여 주어진다. 가격은 0보다 크거나 같고, 1,000보다 작거나 같은 정수이다.

출력

첫째 줄에 기타줄을 적어도 N개 사기 위해 필요한 돈의 최솟값을 출력한다.

예제 입력 1 복사

4 2
12 3
15 4

예제 출력 1 복사

12

예제 입력 2 복사

10 3
20 8
40 7
60 4

예제 출력 2 복사

36

 

문제풀이(1)

첫번째줄에 주어지는 N, M의 관계 - N은 끊어진 기타줄의 개수이고, M은 기타줄을 구매할 수 있는 브랜드의 개수이다.
두번째줄 이후로는 패키지의 가격, 1개의 가격이 적혀있다. 이때, 1개의 가격이 패키지의 가격보다 비쌀 수 있다.
최소한의 금액으로 N개의 기타줄을 사는 가격을 구하는 문제이다.
1. 패키지의 최소 금액과 1개의 최소 금액을 구한다.
2-1. 6개를 낱개로 사는 것이 패키지를 구매하는 것보다 싸다면 모두 낱개로 구매한다.
2-2. 6개를 낱개로 사는 것이 패키지를 구매하는 것보다 비싸다면 N/6의 몫만큼 패키지로 구매하고, 나머지를 패키지로 구매하는 것과 낱개로 구매하는 것중에 싼것을 찾는다.
3. 2-1, 2-2에서 나온 값들의 합을 출력한다.

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

const [N, M] = input.shift().split(" ").map(Number);
const brand = input.map((line) => line.split(" ").map(Number));

const minPackage = brand.reduce((min, curr) => (curr[0] < min ? curr[0] : min), brand[0][0]);
const minPiece = brand.reduce((min, curr) => (curr[1] < min ? curr[1] : min), brand[0][1]);
const pacakgeCount = Math.floor(N / 6);
const pieceCount = N % 6;

let answer = 0;

// 낱개로 사는 것 보다 패키지로 구매하는게 효율이 좋을 경우
if (minPackage / 6 < minPiece) {
  // 패키지 만큼 구매
  answer += pacakgeCount * minPackage;
  // 패키지 만큼 구매하고 남은 낱개를 구매할 때 더 가격이 낮은쪽을 구매
  answer += pieceCount * minPiece < minPackage ? pieceCount * minPiece : minPackage;
} else {
  // 낱개로 사는 것 보다 패키지로 구매하는게 효율이 좋지 않을 경우
  answer += N * minPiece;
}

console.log(answer);

 

문제풀이(2) - 리팩토링

1. 가독성을 위해 변수명 수정
2. 불필요한 조건문 제거

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

const [N, M] = input.shift().split(" ").map(Number);
const brand = input.map((line) => line.split(" ").map(Number));

const minPackage = brand.reduce((min, curr) => (curr[0] < min ? curr[0] : min), brand[0][0]);
const minPiece = brand.reduce((min, curr) => (curr[1] < min ? curr[1] : min), brand[0][1]);
const pacakgeCount = Math.floor(N / 6);
const pieceCount = N % 6;

let totalCost = pacakgeCount * minPackage + Math.min(pieceCount * minPiece, minPackage);

if (minPackage / 6 >= minPiece) {
  totalCost = N * minPiece;
}

console.log(totalCost);

+ Recent posts