diff --git "a/ts-programmers/greedy/001_\354\262\264\354\234\241\353\263\265.ts" "b/ts-programmers/greedy/001_\354\262\264\354\234\241\353\263\265.ts" new file mode 100644 index 0000000..a19d82f --- /dev/null +++ "b/ts-programmers/greedy/001_\354\262\264\354\234\241\353\263\265.ts" @@ -0,0 +1,162 @@ +// https://school.programmers.co.kr/learn/courses/30/lessons/42862 + +{ + // 세번째 풀이 Map + 배열 버전 + function solution4(n: number, lost: number[], reserve: number[]): number { + // 학생 기본 Map + const studentMap = new Map(); + + // 모든 학생에게 체육복 1벌씩 주기 + for (let i = 1; i <= n; i++) { + studentMap.set(i, 1); + } + + // 잃은개수 빼기 + for (const student of lost) { + studentMap.set(student, studentMap.get(student)! - 1); + } + + // 여유분 더하기 + for (const student of reserve) { + studentMap.set(student, studentMap.get(student)! + 1); + } + + // 배열로 변환하여 순차 처리 + const studentArr = Array.from({ length: n }, (_, i) => [ + i + 1, + studentMap.get(i + 1)!, + ]); + + // 체육복 없는 학생이 좌우에서 빌릴 수 있는지 확인 + for (let i = 0; i < studentArr.length; i++) { + const [, count] = studentArr[i]; + + if (count === 0) { + // 왼쪽 학생 + if (i > 0 && studentArr[i - 1][1] === 2) { + studentArr[i - 1][1] -= 1; + studentArr[i][1] += 1; + } + // 오른쪽 학생 + else if (i < n - 1 && studentArr[i + 1][1] === 2) { + studentArr[i + 1][1] -= 1; + studentArr[i][1] += 1; + } + } + } + + // 체육 수업 가능한 학생 수 + return studentArr.filter(([, v]) => v >= 1).length; + } + + // test + console.log(solution4(5, [2, 2, 2, 4], [1, 2, 3, 3, 5])); // 4 + + // 세번째 풀이 - 여러 벌 도난 / 여러 벌 보유의 경우 + // ex) lost = [2, 2, 4] / reserve = [1, 2, 3, 3, 5] + + // 전체 학생에게 1벌을 기본으로 설정 + // 각 학생에 대해 앞/뒤 친구 중 여분이 있으면 1개 빌려옴 + + // 마지막에 체육복이 1개 이상인 학생 수를 세어 반환 + + function solution3(n: number, lost: number[], reserve: number[]): number { + let answer = 0; + + // 학생 기본 Map + const studentMap = new Map(); + + // 모든 학생에게 체육복 1벌씩 주기 + for (let i = 1; i <= n; i++) { + studentMap.set(i, 1); + } + console.log(studentMap); + + // 잃은개수 빼기 + for (const student of lost) { + studentMap.set(student, studentMap.get(student)! - 1); + } + + // 여유분 더하기 + for (const student of reserve) { + studentMap.set(student, (studentMap.get(student) ?? 0) + 1); + } + + for (const [student, count] of studentMap) { + if (count === 0) { + const prev = student - 1; + const next = student + 1; + + if (studentMap.get(prev) && studentMap.get(prev)! > 1) { + studentMap.set(prev, studentMap.get(prev)! - 1); + studentMap.set(student, 1); + } else if (studentMap.get(next) && studentMap.get(next)! > 1) { + studentMap.set(next, studentMap.get(next)! - 1); + studentMap.set(student, 1); + } + } + } + for (const count of studentMap.values()) { + if (count >= 1) answer++; + } + return answer; + } + + // test + // console.log(solution3(5, [2, 2, 4], [1, 2, 3, 3, 5])); // 5 + + // 두번째 풀이 + function solution2(n: number, lost: number[], reserve: number[]): number { + const filteredLost: number[] = lost + .filter((x) => !reserve.includes(x)) + .sort((a, b) => a - b); + const filteredReserve: number[] = reserve.filter((x) => !lost.includes(x)); + + const reserveSet: Set = new Set(filteredReserve); + + let answer = n - filteredLost.length; + + for (const student of filteredLost) { + if (reserveSet.has(student - 1)) { + reserveSet.delete(student - 1); + answer++; + } else if (reserveSet.has(student + 1)) { + reserveSet.delete(student + 1); + answer++; + } + } + + return answer; + } + + // 첫번째 풀이 + function solution(n: number, lost: number[], reserve: number[]): number { + // 잃어버린 사람의 +1 / -1 값이 reserve에 있다면 빌려주기 완 그리고 n에 ++ + // 5명 중 못 빌린 사람을 빼면 값이 나옴 + + // 누락된 조건 추가 (여분을 가지고 있는 사람 중 도난당한 경우) + const filteredLost = lost + .filter((x) => !reserve.includes(x)) + .sort((a, b) => a - b); + const filteredReserve = reserve + .filter((x) => !lost.includes(x)) + .sort((a, b) => a - b); + + for (const lostStudent of filteredLost) { + const idx = filteredReserve.findIndex( + (v) => v === lostStudent - 1 || v === lostStudent + 1 + ); // findIndex(callback) 여기서 인자 v 는 filteredReserve의 각 요소가 들어옴 + + if (idx !== -1) { + filteredReserve.splice(idx, 1); + n++; + } + } + + return n - filteredLost.length; + } + + // test + // console.log(solution(5, [2, 4], [1, 3, 5])); // 5 + // console.log(solution2(5, [2, 4], [1, 3, 5])); // 5 +} diff --git "a/ts-programmers/greedy/002_\354\241\260\354\235\264\354\212\244\355\213\261.ts" "b/ts-programmers/greedy/002_\354\241\260\354\235\264\354\212\244\355\213\261.ts" new file mode 100644 index 0000000..cf21bfd --- /dev/null +++ "b/ts-programmers/greedy/002_\354\241\260\354\235\264\354\212\244\355\213\261.ts" @@ -0,0 +1,40 @@ +// https://school.programmers.co.kr/learn/courses/30/lessons/42860 + +import _ from 'lodash'; + +{ + // 로직을 나눈다면? 위 / 아래 조작 횟수 계산 + function countUpDownMoves(char: string): number { + const up = char.charCodeAt(0) - 'A'.charCodeAt(0); + const down = 'Z'.charCodeAt(0) - char.charCodeAt(0) + 1; + return Math.min(up, down); + } + + function countLeftRightMoves(str: string[]): number { + let minMove = str.length - 1; + for (let i = 0; i < str.length; i++) { + let next = i + 1; + + while (next < str.length && str[next] === 'A') { + next++; + } + + const goAndBack = i + i + (str.length - next); + const backAndGo = i + (str.length - next) * 2; + + minMove = Math.min(minMove, goAndBack, backAndGo); + } + + return minMove; + } + + // 좌 / 우 조작(커서 이동) 최소화 (a일 경우에 넘어가야하는?) + function solution(name: string): number { + return ( + _.sum([...name].map(countUpDownMoves)) + countLeftRightMoves([...name]) + ); + } + + // test + console.log(solution('JAZ')); // 11 +} diff --git "a/ts-programmers/greedy/003_\355\201\260_\354\210\230_\353\247\214\353\223\244\352\270\260.ts" "b/ts-programmers/greedy/003_\355\201\260_\354\210\230_\353\247\214\353\223\244\352\270\260.ts" new file mode 100644 index 0000000..d546f30 --- /dev/null +++ "b/ts-programmers/greedy/003_\355\201\260_\354\210\230_\353\247\214\353\223\244\352\270\260.ts" @@ -0,0 +1,76 @@ +// https://school.programmers.co.kr/learn/courses/30/lessons/42883 + +{ + function solution(number: string, k: number): string { + const answer: number[] = []; + + for (const char of number) { + const num = Number(char); + + // 이름 붙이기 + // 스택의 마지막 숫자가 현재 숫자보다 작으면 제거한다 (더 큰 숫자를 앞으로 배치) + // 제거 가능한 기회(k)가 남아있는 동안 반복한다. + while (k > 0 && answer.length && answer.at(-1)! < num) { + answer.pop(); + k--; + } + } + + // 만약 k 가 남았을 경우 뒤에서 잘라줘야함 + if (k > 0) { + answer.splice(-k); + } + + return answer.join(''); + } + + // test + console.log(solution('4177252841', 4)); // 775841 + + // solution2 - 함수형으로 분할해보고 assert 까지 적용해보기 + function isDescending(arr: number[]): boolean { + for (let i = 0; i < arr.length - 1; i++) { + if (arr[i] < arr[i + 1]) return false; + } + return true; + } + + function optimizeStack(stack: number[], num: number, k: number): number { + // while 없애면 assert 터트릴 수 있음 + while (k > 0 && stack.length && stack.at(-1)! < num) { + stack.pop(); + k--; + } + stack.push(num); + console.assert( + isDescending(stack), + '스택의 숫자는 항상 내림차순이어야 함당' + ); + return k; + } + + function solution2(number: string, k: number): string { + const answer: number[] = []; + + for (const char of number) { + const num = Number(char); + k = optimizeStack(answer, num, k); + } + + if (k > 0) { + answer.splice(-k); + } + + return answer.join(''); + } + + // test + console.log(solution2('4177252841', 4)); // 775841 +} +// // ok, k > 0 +// [4] +// [4, 1] + +// // not ok +// [7, 2, 5] +// [1,2,3]