일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 자바스크립트
- computerscience
- 프론트엔드개발자
- cpu
- html/css/js
- git
- nodejs
- LinkSnap
- github
- 코테
- 너비우선탐색
- CS
- Javascript
- 국비지원
- 호이스팅
- 코딩테스트
- 그리디
- DFS
- 야놀자
- 백준
- 국비지원취업
- 패스트캠퍼스
- 컴퓨터공학
- 알고리즘
- CSS
- BFS
- 부트캠프
- js
- 컴퓨터과학
- KAKAO
- Today
- Total
My Boundary As Much As I Experienced
우승자는 누구?(백준 코딩테스트 5179번, 구현/정렬, NodeJS 풀이) 본문
https://www.acmicpc.net/problem/5179
문제 수준:
실버3
문제 요약:
문제 풀이 대회를 했는데, N명의 참가자가 M개의 문제를 풀게 됐다.
이때 점수가 존재하는데,
문제를 맞히면 '문제를 맞힌 시각 + (틀린 횟수 x 20)'을 점수로써 해당 참가자에게 주어진다.
(*맞춘 다음 다시 푼다고 하면 첫 풀이만 인정된다. 중복 반영은 안 된다.)
순위를 정할 땐
푼 문제 수가 많은 순으로 등수가 높고,
푼 문제 수가 같은 경우엔 점수가 낮은 쪽이 높다
(그렇다면 점수가 아니라 패널티 아닌가..)
이 테스트기를 구현하시오.
입출력 예 (입력 / 출력):
문제 풀이 전략:
뭐랄까. 점수라는건 사실 패널티라고 봐야할거 같다.
틀릴수록 점수가 오른다는 것이 직관적으로 안 받아들여지고,
점수가 높으면 안 좋은거라는 것에서 또 직관에 어긋나는 느낌이다.
이걸 패널티라고 바꾸면 나름 받아들여진다.
그리고 이런 구현 문제는 한 번의 로직으로 싹 다 해결하려고 하면 안 된다.
왜냐하면 문제에 딱히 고난이도의 알고리즘은 없더라도,
매우 복합적으로 신경 쓸 정책?들이 많기 때문이다.
로직 관심사 분리가 꼭 필요하다.
아래는 이번 문제를 풀기위해 짠 객체 구조이다.
플레이어마다 총점(result), 푼문제(solved), 문제별상태(오답횟수, 해결여부, 해결한 시각) 등등..
매우 많은 정보들을 기록하고, 이를 조합해서 패널티 점수를 계산해야되고...
이런 것들을 한 큐에 스크립트 줄줄 내려오면서 계산하는게 아니라 함수로 나눠놔야 한다.
{
'0': { // 테스트케이스 인덱스
'1': { // 플레이어1
result: 24, // 총점
solved: 1, // 푼 문제 갯수
A: { negative: 0, judge: false, solvedTime: 0 }, // 문제 별 상태: { 오답 횟수, 해결 여부, 해결한 시각 }
B: { negative: 0, judge: true, solvedTime: 24 },
C: { negative: 1, judge: false, solvedTime: 0 },
D: { negative: 0, judge: false, solvedTime: 0 }
},
'2': { // 플레이어2
result: 100,
solved: 2,
A: { negative: 0, judge: false, solvedTime: 0 },
B: { negative: 0, judge: true, solvedTime: 55 },
C: { negative: 1, judge: true, solvedTime: 25 },
D: { negative: 0, judge: false, solvedTime: 0 }
},
'3': { // 플레이어3
result: 55,
solved: 1,
A: { negative: 0, judge: false, solvedTime: 0 },
B: { negative: 0, judge: false, solvedTime: 0 },
C: { negative: 0, judge: false, solvedTime: 0 },
D: { negative: 0, judge: true, solvedTime: 55 }
}
},
'1' : {
...
}
}
나는 이 문제를 로직을 3가지 함수로 분리하여 풀었다.
- setBoard : 보드의 뼈대를 만들고 초기값을 입력해놓는다. (테스트케이스가 초기화될때마다 실행된다)
- describeScore: 주어진 스크립트에 의거하여 점수와 풀이여부를 Board에 기입해놓는다.
- consoleScoreBoard: 마지막에 보드에 의거하여 최종 순위와 점수 등등을 console.log로 찍는다.
이런 식으로 함수를 나눠서 구현했다.
문제가 요구하는 알고리즘이 어렵진 않지만 그 갯수가 아주 여러개이며 서로 의존하고 있기 때문에
집중하지 않으면 분명 까먹는 정책이 있거나 잘못 이해한 경우가 발생할 것이다.
방심하면 시간 소모가 큰 문제가 될 것.
내 풀이:
let fs = require("fs");
let [n, ...cases] = fs.readFileSync("input.txt").toString().trim().split("\n");
let board = null; // 일단 null 상태지만 setTemplate 함수를 실행시키면 초기화 된다.
let index = 0; // 현재 테스트케이스를 가리킴
for (let i = 0; i < cases.length; i++) {
const spl = cases[i].split(" ");
if (spl.length === 3) { // 스크립트를 " "으로 나눈게 3개라는 것은 새로운 테스트케이스가 시작됐단 의미
const [m, n, p] = spl;
index++; // 인덱스를 올린다.
setBoard(index, +p, +m); // setBoard 실행
} else { // 새로운 테스트케이스가 아니라면
const [player, prob, time, judge] = spl;
describeScore(player, prob, +time, +judge); // 각 플레이어의 액션을 보드에 기입
}
}
consoleScoreBoard(); // 위 for문 다 돌았으면 consoleScoreBoard를 실행
function setBoard(index, player, matter) {
const converter = { // 인덱스 -> 문제 이름 컨버터..
0: "A",
1: "B",
2: "C",
3: "D",
4: "E",
5: "F",
6: "G",
7: "H",
8: "I",
9: "J",
};
if (!board) { // 보드가 null일 때,
board = {}; // 보드 초기화
}
board[index] = {}; // board의 index(테스트케이스의 index값)로 객체 생성
for (let i = 0; i < player; i++) {
board[index][i + 1] = {}; // 플레이어 객체 생성
board[index][i + 1].result = 0; // 플레이어의 총점
board[index][i + 1].solved = 0; // 플레이어의 푼문제 갯수
for (let k = 0; k < matter; k++) {
board[index][i + 1][converter[k]] = { // 문제 생성
negative: 0, // 해당 문제의 오답 횟수
judge: false, // 해당 문제의 해결 여부
solvedTime: 0, // 해당 문제의 해결한 시각
};
}
}
}
function consoleScoreBoard() { // 순위 출력 함수
for (let i = 1; i <= +n; i++) {
console.log(`Data Set ${i}:`);
const target = Object.entries(board[i]).sort((a, b) => { // 정렬 로직
if (b[1].solved > a[1].solved) { // 푼 문제 횟수 비교
return 1;
} else if (b[1].solved === a[1].solved) { // 만약 푼 문제가 같다면
if (b[1].result > a[1].result) { // 총점(패널티)가 누가 더 큰지?
return -1;
} else {
return 1;
}
} else {
return -1;
}
});
for (let j = 0; j < target.length; j++) {
const player = target[j][0];
const solved = target[j][1].solved;
const score = target[j][1].result;
console.log(`${player} ${solved} ${score}`); // 플레이어들의 성적 출력
}
console.log(); // 띄어쓰기
}
}
function describeScore(player, prob, time, judge) { // 플레이어들의 성적을 기입하는 곳
if (judge && !board[index][player][prob].judge) { // 풀었으며, 풀이 여부가 아직 false라면
board[index][player][prob].judge = true; // 풀이여부는 true로 전환
board[index][player][prob].solvedTime = time; // 해결 시간 설정
board[index][player].result += +time + board[index][player][prob].negative * 20; // 점수 업데이트(패널티)
board[index][player].solved++; // 푼 문제 1 증가
} else {
board[index][player][prob].negative++; // 오답인 경우 오답횟수만 증가
}
}
'Algorithm > Coding Test' 카테고리의 다른 글
전화번호 목록(프로그래머스 레벨2, 해시, JavaScript 풀이) (0) | 2024.04.14 |
---|---|
행렬 곱셈(백준 2740번, 수학/구현, NodeJS풀이) (0) | 2024.03.29 |
태보태보 총난타(백준 코딩테스트 17249번, 문자열, NodeJS 풀이) (0) | 2024.03.24 |
귀여운 수~ε٩(๑> ₃ <)۶з(백준 코딩테스트 17294번, 문자열, NodeJS 풀이) (0) | 2024.03.24 |
누울 자리를 찾아라(백준 코딩테스트 1652번, 문자열/그리디, NodeJS 풀이) (1) | 2024.03.24 |