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

 

1244번: 스위치 켜고 끄기

첫째 줄에는 스위치 개수가 주어진다. 스위치 개수는 100 이하인 양의 정수이다. 둘째 줄에는 각 스위치의 상태가 주어진다. 켜져 있으면 1, 꺼져있으면 0이라고 표시하고 사이에 빈칸이 하나씩

www.acmicpc.net

문제

1부터 연속적으로 번호가 붙어있는 스위치들이 있다. 스위치는 켜져 있거나 꺼져있는 상태이다. <그림 1>에 스위치 8개의 상태가 표시되어 있다. ‘1’은 스위치가 켜져 있음을, ‘0’은 꺼져 있음을 나타낸다. 그리고 학생 몇 명을 뽑아서, 학생들에게 1 이상이고 스위치 개수 이하인 자연수를 하나씩 나누어주었다. 학생들은 자신의 성별과 받은 수에 따라 아래와 같은 방식으로 스위치를 조작하게 된다.

남학생은 스위치 번호가 자기가 받은 수의 배수이면, 그 스위치의 상태를 바꾼다. 즉, 스위치가 켜져 있으면 끄고, 꺼져 있으면 켠다. <그림 1>과 같은 상태에서 남학생이 3을 받았다면, 이 학생은 <그림 2>와 같이 3번, 6번 스위치의 상태를 바꾼다.

여학생은 자기가 받은 수와 같은 번호가 붙은 스위치를 중심으로 좌우가 대칭이면서 가장 많은 스위치를 포함하는 구간을 찾아서, 그 구간에 속한 스위치의 상태를 모두 바꾼다. 이때 구간에 속한 스위치 개수는 항상 홀수가 된다.

스위치 번호스위치 상태
0 1 0 1 0 0 0 1

<그림 1>

예를 들어 <그림 2>에서 여학생이 3을 받았다면, 3번 스위치를 중심으로 2번, 4번 스위치의 상태가 같고 1번, 5번 스위치의 상태가 같으므로, <그림 3>과 같이 1번부터 5번까지 스위치의 상태를 모두 바꾼다. 만약 <그림 2>에서 여학생이 4를 받았다면, 3번, 5번 스위치의 상태가 서로 다르므로 4번 스위치의 상태만 바꾼다.

스위치 번호스위치 상태
0 1 1 1 0 1 0 1

<그림 2>

스위치 번호스위치 상태
1 0 0 0 1 1 0 1

<그림 3>

입력으로 스위치들의 처음 상태가 주어지고, 각 학생의 성별과 받은 수가 주어진다. 학생들은 입력되는 순서대로 자기의 성별과 받은 수에 따라 스위치의 상태를 바꾸었을 때, 스위치들의 마지막 상태를 출력하는 프로그램을 작성하시오.

입력

첫째 줄에는 스위치 개수가 주어진다. 스위치 개수는 100 이하인 양의 정수이다. 둘째 줄에는 각 스위치의 상태가 주어진다. 켜져 있으면 1, 꺼져있으면 0이라고 표시하고 사이에 빈칸이 하나씩 있다. 셋째 줄에는 학생수가 주어진다. 학생수는 100 이하인 양의 정수이다. 넷째 줄부터 마지막 줄까지 한 줄에 한 학생의 성별, 학생이 받은 수가 주어진다. 남학생은 1로, 여학생은 2로 표시하고, 학생이 받은 수는 스위치 개수 이하인 양의 정수이다. 학생의 성별과 받은 수 사이에 빈칸이 하나씩 있다.

출력

스위치의 상태를 1번 스위치에서 시작하여 마지막 스위치까지 한 줄에 20개씩 출력한다. 예를 들어 21번 스위치가 있다면 이 스위치의 상태는 둘째 줄 맨 앞에 출력한다. 켜진 스위치는 1, 꺼진 스위치는 0으로 표시하고, 스위치 상태 사이에 빈칸을 하나씩 둔다.

예제 입력 1 복사

8
0 1 0 1 0 0 0 1
2
1 3
2 3

예제 출력 1 복사

1 0 0 0 1 1 0 1

 

문제풀이(1)

4번째 부터 들어오는 입력 값은 학생성별, 학생이 받은수가 주어진다.
이때, 학생의 성별이 남성이라면 받은 수의 배수의 스위치의 상태를 바꾼다.
자기가 받은 수와 같은 번호가 붙은 스위치를 중심으로 좌우가 대칭이면서 가장 많은 스위치를 포함하는 구간을 찾아서,
그 구간에 속한 스위치의 상태를 모두 바꾼다. 이때 구간에 속한 스위치 개수는 항상 홀수가 된다.
위의 조건대로 풀어나가면 된다.
남학생의 경우는 자신이 주어진 번호의 배수에만 스위치를 변경하면 되지만
여학생의 경우 자신이 주어진 번호 + 주변번호까지 변경하는 로직을 구현하면 문제는 해결된다.

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

const switchsArr = switchs.split(" ").map(Number);
const studentTypeAndNumber = arr.map((student) => student.split(" ").map(Number));

// 학생의 스위치 조작 함수
const action = (type, number, switchsArr) => {
  // 남학생 스위치 조작
  if (type === 1) {
    for (let i = 0; i < switchsArr.length; i++) {
      if ((i + 1) % number === 0) {
        switchsArr[i] = switchsArr[i] === 0 ? 1 : 0;
      }
    }
  }

  // 여학생 스위치 조작
  if (type === 2) {
    // 주어진 번호 스위치 변경
    switchsArr[number - 1] = switchsArr[number - 1] === 0 ? 1 : 0;

    for (let i = 1; i < switchsArr.length; i++) {
      // 좌우가 비대칭일 경우 정지
      if (switchsArr[number - 1 - i] !== switchsArr[number - 1 + i]) {
        break;
      } else {
        let ChangeSwitchState = switchsArr[number - 1 - i] === 0 ? 1 : 0;
        switchsArr[number - 1 - i] = ChangeSwitchState;
        switchsArr[number - 1 + i] = ChangeSwitchState;
      }
    }
  }
};

studentTypeAndNumber.forEach((value) => {
  const [type, number] = value;
  action(type, number, switchsArr);
});

const answer = [];

while (switchsArr.length > 0) {
  answer.push(switchsArr.splice(0, 20).join(" "));
}

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

 

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

1. 재사용성을 올리기 위해 남학생과 여학생의 스위치 조작 함수 분리 및 스위치 상태 변경 함수 추가
2. 변수명 직관적으로 변경

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

const switchStateArray = switchs.split(" ").map(Number);
const studentTypeAndNumber = arr.map((student) => student.split(" ").map(Number));

// 스위치 변경 함수
const flipSwitch = (index, switchStateArray) => {
  switchStateArray[index] = switchStateArray[index] === 0 ? 1 : 0;
};

// 남학생 스우치 변경
const maleStudentAction = (number, switchStateArray) => {
  for (let i = 0; i < switchStateArray.length; i++) {
    if ((i + 1) % number === 0) {
      flipSwitch(i, switchStateArray);
    }
  }
};

// 여학생 스위치 변경
const femaleStudentAction = (number, switchStateArray) => {
  flipSwitch(number - 1, switchStateArray);

  for (let i = 1; i < switchStateArray.length; i++) {
    if (switchStateArray[number - 1 - i] !== switchStateArray[number - 1 + i]) {
      break;
    } else {
      flipSwitch(number - 1 - i, switchStateArray);
      flipSwitch(number - 1 + i, switchStateArray);
    }
  }
};

studentTypeAndNumber.forEach((value) => {
  const [type, number] = value;
  type === 1 ? maleStudentAction(number, switchStateArray) : femaleStudentAction(number, switchStateArray);
});

const answer = [];

while (switchStateArray.length > 0) {
  answer.push(switchStateArray.splice(0, 20).join(" "));
}

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

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

 

1302번: 베스트셀러

첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고

www.acmicpc.net

문제

김형택은 탑문고의 직원이다. 김형택은 계산대에서 계산을 하는 직원이다. 김형택은 그날 근무가 끝난 후에, 오늘 판매한 책의 제목을 보면서 가장 많이 팔린 책의 제목을 칠판에 써놓는 일도 같이 하고 있다.

오늘 하루 동안 팔린 책의 제목이 입력으로 들어왔을 때, 가장 많이 팔린 책의 제목을 출력하는 프로그램을 작성하시오.

입력

첫째 줄에 오늘 하루 동안 팔린 책의 개수 N이 주어진다. 이 값은 1,000보다 작거나 같은 자연수이다. 둘째부터 N개의 줄에 책의 제목이 입력으로 들어온다. 책의 제목의 길이는 50보다 작거나 같고, 알파벳 소문자로만 이루어져 있다.

출력

첫째 줄에 가장 많이 팔린 책의 제목을 출력한다. 만약 가장 많이 팔린 책이 여러 개일 경우에는 사전 순으로 가장 앞서는 제목을 출력한다.

예제 입력 1 복사

5
top
top
top
top
kimtop

예제 출력 1 복사

top

 

문제풀이(1)

많이 팔린 책의 이름을 찾는 문제이다.
map객체를 사용해서 책들의 개수를 카운트 한다음 많이 나온 책을 출력하면 될 것 같다.
우선 팔린수가 같은 책이 여러개 일 경우 사전순으로 나타내야 하기 때문에 먼저 정렬을 실행한다.
많이 팔린 책의 수를 찾고 map을 순회하면서 가장 많이 팔린 책을 출력하면된다.

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

const map = new Map();

let maxCount = 0;
let answer = "";

// 사전순으로 정렬
arr.sort();

arr.forEach((book) => {
  if (map.has(book)) {
    map.set(book, map.get(book) + 1);
  } else {
    map.set(book, 1);
  }

  let bookCount = map.get(book);
  if (bookCount > maxCount) {
    maxCount = bookCount;
  }
});

for (const [key, value] of map.entries()) {
  if (value === maxCount) {
    answer = key;
    break;
  }
}

console.log(answer);

 

문제풀이(2)

1. map 객체 대신 일반 객체를 사용하여 메소드 사용을 최소화 한다.
2. 반복문을 줄일 수 있도록 팔린 책의 수를 카운트 하면서 제일 많이 팔린 책을 동시에 찾는다.
메모리 - 9344kb -> 9348kb
실행시간 - 120ms -> 116ms

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

let answer = "";

// 사전순으로 정렬
arr.sort();

const obj = {};

arr.forEach((book) => {
  obj[book] = (obj[book] || 0) + 1;

  if (!answer || obj[book] > obj[answer]) {
    answer = book;
  }
});

console.log(answer);

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

 

1748번: 수 이어 쓰기 1

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

www.acmicpc.net

문제

1부터 N까지의 수를 이어서 쓰면 다음과 같이 새로운 하나의 수를 얻을 수 있다.

1234567891011121314151617181920212223...

이렇게 만들어진 새로운 수는 몇 자리 수일까? 이 수의 자릿수를 구하는 프로그램을 작성하시오.

입력

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

출력

첫째 줄에 새로운 수의 자릿수를 출력한다.

예제 입력 1 복사

5

예제 출력 1 복사

5

예제 입력 2 복사

15

예제 출력 2 복사

21

예제 입력 3 복사

120

예제 출력 3 복사

252

 

문제풀이(1)

수의 범위가 1억까지이기 때문에 for로 전체 순회를 할 경우 시간초과가 날것으로 예상된다.
수의 자릿수를 구하는 공식을 작성하여 구해본다.
1~9는 한자리수, 10~99는 두자리수, 100~999는 세자리수이다.
위의 규칙성을 봤을 때 1의 자리수부터 10의 배수로 자리수를 계산할 수 있음을 알 수 있다. (1 -> 10 -> 100 -> 100)
N - (자리수) + 1 = 자리수의 개수이다.
15 -> 15 - 1 + 1 = 15 = 한자리수의 개수
15 -> 15 - 10 + 1 = 6 = 두자리수의 개수
위와 같은 형식으로 규칙성을 가질 수 있다.

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

let answer = 0;

for (let i = 1; i <= N; i *= 10) {
  answer += N - i + 1;
}

console.log(answer);

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

 

3036번: 링

출력은 총 N-1줄을 해야 한다. 첫 번째 링을 제외한 각각의 링에 대해서, 첫 번째 링을 한 바퀴 돌리면 그 링은 몇 바퀴 도는지 기약 분수 형태 A/B로 출력한다.

www.acmicpc.net

문제

상근이는 창고에서 링 N개를 발견했다. 상근이는 각각의 링이 앞에 있는 링과 뒤에 있는 링과 접하도록 바닥에 내려놓았다. 

상근이는 첫 번째 링을 돌리기 시작했고, 나머지 링도 같이 돌아간다는 사실을 발견했다. 나머지 링은 첫 번째 링 보다 빠르게 돌아가기도 했고, 느리게 돌아가기도 했다. 이렇게 링을 돌리다 보니 첫 번째 링을 한 바퀴 돌리면, 나머지 링은 몇 바퀴 도는지 궁금해졌다.

링의 반지름이 주어진다. 이때, 첫 번째 링을 한 바퀴 돌리면, 나머지 링은 몇 바퀴 돌아가는지 구하는 프로그램을 작성하시오.

입력

첫째 줄에 링의 개수 N이 주어진다. (3 ≤ N ≤ 100)

다음 줄에는 링의 반지름이 상근이가 바닥에 놓은 순서대로 주어진다. 반지름은 1과 1000를 포함하는 사이의 자연수이다.

출력

출력은 총 N-1줄을 해야 한다. 첫 번째 링을 제외한 각각의 링에 대해서, 첫 번째 링을 한 바퀴 돌리면 그 링은 몇 바퀴 도는지 기약 분수 형태 A/B로 출력한다.

예제 입력 1 복사

3
8 4 2

예제 출력 1 복사

2/1
4/1

 

문제풀이(1)

문제의 풀이가 생각나지 않아서 찾아본 결과 최대공약수를 이용한 문제라는 것을 알게되었다.
2수의 최대공약수를 찾아서 기약분수(?)의 형태로 출력한면 된다고 한다.
첫번째 링이 돌아가면 나머지 링도 돌기 때문에 첫번째 링과 나머지 링의 최대공약수를 찾아 기약분수의 형태로 나타내면 된다.
최대공약수는 유클리드 호제법을 사용해서 구하는 방식을 사용하려고 한다.
8과 4의 최대 공약수 = 4 -> (8/4)/(4/4) -> 2/1
8과 2의 최대 공약수 = 2 -> (8/2)/(2/2) -> 4/1

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

const N = Number(input.shift());
const arr = input[0].split(" ").map(Number);
const firstNum = arr[0];

// 유클리드 호제법 최대공약수 구하기
const gcd = (a, b) => {
  if (a % b === 0) {
    return b;
  }
  return gcd(b, a % b);
};

for (let i = 1; i < N; i++) {
  const nowNum = arr[i];
  const resultGCD = gcd(firstNum, nowNum);
  console.log(`${firstNum / resultGCD}/${nowNum / resultGCD}`);
}

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

 

9613번: GCD 합

첫째 줄에 테스트 케이스의 개수 t (1 ≤ t ≤ 100)이 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있다. 각 테스트 케이스는 수의 개수 n (1 < n ≤ 100)가 주어지고, 다음에는 n개의 수가 주어진

www.acmicpc.net

문제

양의 정수 n개가 주어졌을 때, 가능한 모든 쌍의 GCD의 합을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 테스트 케이스의 개수 t (1 ≤ t ≤ 100)이 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있다. 각 테스트 케이스는 수의 개수 n (1 < n ≤ 100)가 주어지고, 다음에는 n개의 수가 주어진다. 입력으로 주어지는 수는 1,000,000을 넘지 않는다.

출력

각 테스트 케이스마다 가능한 모든 쌍의 GCD의 합을 출력한다.

예제 입력 1 복사

3
4 10 20 30 40
3 7 5 12
3 125 15 25

예제 출력 1 복사

70
3
35

 

문제풀이(1)

최대공약수(Greatest Common Divisor, GCD)를 구하는 문제이다.
GCD를 구하는 알고리즘에는 유클리드 호제법이 있다.
유클리드 호제법을 구현하여 가능한 모든 쌍의 GCD의 합을 구하면된다.
유클리드 호제법
1. 두 수 중 큰 수를 a, 작은 수를 b로 둡니다.
2. a를 b로 나눈 나머지를 r로 둡니다.
3. a와 b의 최대공약수는 b와 r의 최대공약수와 같습니다.
4. b가 0이 될 때까지 2번과 3번의 과정을 반복합니다.

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

const arrSort = arr.map((value) => value.split(" ").map(Number));

const gcd = (a, b) => {
  if (a % b === 0) {
    return b;
  }
  return gcd(b, a % b);
};

const answer = [];

arrSort.forEach((numbers) => {
  const [N, ...nums] = numbers;
  let sum = 0;
  for (let i = 0; i < N - 1; i++) {
    for (let j = i + 1; j < N; j++) {
      sum += gcd(nums[i], nums[j]);
    }
  }
  answer.push(sum);
});

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

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

 

17219번: 비밀번호 찾기

첫째 줄에 저장된 사이트 주소의 수 N(1 ≤ N ≤ 100,000)과 비밀번호를 찾으려는 사이트 주소의 수 M(1 ≤ M ≤ 100,000)이 주어진다. 두번째 줄부터 N개의 줄에 걸쳐 각 줄에 사이트 주소와 비밀번

www.acmicpc.net

문제

2019 HEPC - MAVEN League의 "비밀번호 만들기"와 같은 방식으로 비밀번호를 만든 경민이는 한 가지 문제점을 발견하였다. 비밀번호가 랜덤으로 만들어져서 기억을 못 한다는 것이었다! 그래서 경민이는 메모장에 사이트의 주소와 비밀번호를 저장해두기로 했다. 하지만 컴맹인 경민이는 메모장에서 찾기 기능을 활용하지 못하고 직접 눈으로 사이트의 주소와 비밀번호를 찾았다. 메모장에 저장된 사이트의 수가 늘어나면서 경민이는 비밀번호를 찾는 일에 시간을 너무 많이 쓰게 되었다. 이를 딱하게 여긴 문석이는 경민이를 위해 메모장에서 비밀번호를 찾는 프로그램을 만들기로 결심하였다! 문석이를 도와 경민이의 메모장에서 비밀번호를 찾아주는 프로그램을 만들어보자.

입력

첫째 줄에 저장된 사이트 주소의 수 N(1 ≤ N ≤ 100,000)과 비밀번호를 찾으려는 사이트 주소의 수 M(1 ≤ M ≤ 100,000)이 주어진다.

두번째 줄부터 N개의 줄에 걸쳐 각 줄에 사이트 주소와 비밀번호가 공백으로 구분되어 주어진다. 사이트 주소는 알파벳 소문자, 알파벳 대문자, 대시('-'), 마침표('.')로 이루어져 있고, 중복되지 않는다. 비밀번호는 알파벳 대문자로만 이루어져 있다. 모두 길이는 최대 20자이다.

N+2번째 줄부터 M개의 줄에 걸쳐 비밀번호를 찾으려는 사이트 주소가 한줄에 하나씩 입력된다. 이때, 반드시 이미 저장된 사이트 주소가 입력된다.

출력

첫 번째 줄부터 M개의 줄에 걸쳐 비밀번호를 찾으려는 사이트 주소의 비밀번호를 차례대로 각 줄에 하나씩 출력한다.

예제 입력 1 복사

16 4
noj.am IU
acmicpc.net UAENA
startlink.io THEKINGOD
google.com ZEZE
nate.com VOICEMAIL
naver.com REDQUEEN
daum.net MODERNTIMES
utube.com BLACKOUT
zum.com LASTFANTASY
dreamwiz.com RAINDROP
hanyang.ac.kr SOMEDAY
dhlottery.co.kr BOO
duksoo.hs.kr HAVANA
hanyang-u.ms.kr OBLIVIATE
yd.es.kr LOVEATTACK
mcc.hanyang.ac.kr ADREAMER
startlink.io
acmicpc.net
noj.am
mcc.hanyang.ac.kr

예제 출력 1 복사

THEKINGOD
UAENA
IU
ADREAMER

 

문제풀이(1)

주소와 비밀번호가 매칭돠어 있는 N중에서 M에 속한 주소의 비밀번호를 찾아내면 되는 문제이다.
map에 주소와 비밀번호를 매칭해서 저장한다음 M배열에서 찾아내면 될 것이다.

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 addreesAndPassword = input.slice(0, N);
const address = input.slice(N);

const map = new Map();

const answer = [];

for (let i = 0; i < N; i++) {
  const [address, password] = addreesAndPassword[i].split(" ");
  map.set(address, password);
}

for (let i = 0; i < M; i++) {
  answer.push(map.get(address[i]));
}

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

 

문제풀이(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 addressesAndPassword = input.slice(0, N);
const addresses = input.slice(N);

const map = new Map();

const answer = [];

for (let i = 0; i < N; i++) {
  const [address, password] = addressesAndPassword[i].split(" ");
  map.set(address, password);
}

const getPassword = (address) => {
  return map.get(address);
};

for (const address of addresses) {
  answer.push(getPassword(address));
}

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

+ Recent posts