Logo

Changchangwoo's blog

TIL
Jan
Feb
Mar
Apr
May
Jun
Jul
Aug
Sep
Oct
Nov
Dec
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Today I Learned를 기록하는 공간입니다. 🔥🔥🔥
잔디를 클릭하면 내용확인이 가능합니다.

프로그래머스 고득점 kit 그리디 복습2

행복 유치원 ( 백준 13164)

  • 문제 풀이 못떠올림, 접근 못함
  • K개의 조가 있어야 한다는 것 ⇒ K-1개 만큼의 조 구분이 필요하다
  • 양 옆의 차이를 받아옴, 그리고 그 양 옆의 차이가 가장 큰 수 만큼 제거가 최소 비용

⇒ 양 옆의 차이를 배열로 만들고, 그 배열을 정렬해서 값을 구한다

const input = require("fs")
  .readFileSync("input.txt")
  .toString()
  .trim()
  .split("\n");
const [N, K] = input.shift().split(" ").map(Number);
const arr = input.shift().split(" ").map(Number);

const diff = [];
for (let i = 0; i < arr.length - 1; i++) {
  diff.push(arr[i + 1] - arr[i]);
}
const sorted_diff = diff
  .sort((a, b) => a - b)
  .slice(0, N - K)
  .reduce((acc, cur) => acc + cur, 0);
console.log(sorted_diff);
  • 문제 풀이의 핵심은 K-1개의 조를 나누었을 때, 조 내부의 최소값을 구분할 필요가 없다는 것
  • 양 옆의 값들을 전부다 계산이 될 수 밖에 없음. 그렇기에 차이값들의 배열 중 K-1개 까지의 값만 생각하면 된다.

reduce

[1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0);
// acc => 누적합, cur => 현재 값, 콜백 => 누적합과의 현재값 계산, 추가 인자 => 시작 값

크루스칼 알고리즘 (= 최소비용 신장트리 계산 )

  • 노드의 개수가 7개라면 간선의 개수는 6개가 되어진다.
  • 간선의 비용 중 가장 작은 수 부터 순차적으로 찾아 올라가며, 사이클이 발생되어지지 않도록 계산하는 것이 핵심

⇒ 구현하기 위해서는 유니온 파인드의 이해가 필요

유니온 파인드 (합집합 찾기)

const getParent = (parent, x) => {
  if (parent[x] === x) return x;
  return (parent[x] = getParent(parent, parent[x]));
};

const union = (parent, a, b) => {
  let pA = getParent(parent, a);
  let pB = getParent(parent, b);
  if (pA > pB) parent[pA] = pB;
  else parent[pB] = pA;
};

const find = (parent, a, b) => {
  let pA = getParent(parent, a);
  let pB = getParent(parent, b);
  if (pA === pB) return 1;
  return 0;
};

const arr = [];
for (let i = 1; i <= 10; i++) {
  arr[i] = i;
}
union(arr, 1, 2);
console.log(find(arr, 1, 2));
  • 각 노드에 대한 부모 배열을 만들고, 각 노드들은 자신이 속한 집합의 대표 노드 ( 가장 작은 값 )을 가리킴
  • 초기에는 모든 노드가 자기 자신을 부모로 가지지만, union을 통해 최상위 부모를 찾아 하나로 연결
  • find를 통해서 같은 집합인지 (= 같은 루트 노드를 가지는지) 확인이 가능
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const input = [];
rl.on("line", (line) => {
  input.push(line);
}).on("close", () => {
  const [N, M] = input.shift().split(" ").map(Number);

  const getParent = (parent, idx) => {
    if (parent[idx] === idx) return idx;
    return (parent[idx] = getParent(parent, parent[idx]));
  };

  const unionParent = (parent, a, b) => {
    const pA = getParent(parent, a);
    const pB = getParent(parent, b);
    if (pA < pB) parent[pB] = pA;
    else parent[pA] = pB;
  };

  const findParent = (parent, a, b) => {
    const pA = getParent(parent, a);
    const pB = getParent(parent, b);
    return pA === pB ? "YES" : "NO";
  };

  const parent = Array.from({ length: N + 1 }, (_, idx) => idx);
  const answer = [];

  for (const line of input) {
    const [key, a, b] = line.split(" ").map(Number);
    if (key === 0) unionParent(parent, a, b);
    else answer.push(findParent(parent, a, b));
  }

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

예제문제 ⇒ 백준1717번, 전형적인 unionFind 할 수 있어? 문제

  • 이해와 구현이 어렵지 않은 알고리즘이니 챙겨가자.

MST ( Minimum Spanning Tree ) 최소 신장 트리

  • 그래프에서 최소 비용으로 모든 구간을 연결한 트리
  • Tree 이기 때문에 Cycle이 존재하면 안되는 것이 핵심
  • 정점의 개수가 N개 일 때, 간선의 개수는 N-1개

섬 연결하기

function getParent(parent, idx) {
  if (parent[idx] === idx) return idx;
  return (parent[idx] = getParent(parent, parent[idx]));
}
function unionParent(parent, idx1, idx2) {
  const p1 = getParent(parent, idx1);
  const p2 = getParent(parent, idx2);
  if (p1 < p2) parent[p2] = p1;
  else parent[p1] = p2;
}
function findParent(parent, idx1, idx2) {
  const p1 = getParent(parent, idx1);
  const p2 = getParent(parent, idx2);
  if (p1 === p2) return true;
  return false;
}
function solution(n, costs) {
  costs.sort((a, b) => a[2] - b[2]);
  const parent = Array.from({ length: n }, (_, i) => i);
  let total = 0;
  let count = 0;
  for (const [a, b, cost] of costs) {
    if (count === n - 1) break;
    if (getParent(parent, a) !== getParent(parent, b)) {
      unionParent(parent, a, b);
      total += cost;
      count++;
    }
  }
  return total;
}
  • 크루스칼 예제 문제
  • 간선 연결 최소 비용 구하기
  • 가중치 값을 기준으로 정렬 후, Union-Find 자료구조를 이용해 간선 선택
  • 섬을 이동하는데 드는 비용을 기준으로 정렬 후 UF 조건을 맞추면서 N-1까지의 값을 구함
  • 간선에 드는 최소 비용을 고르면서도, 집합 (= 연결되지 않은 ) 것들만을 연결, Find면 버리기

단속 카메라

  • 그리디 문제, 회의실 배정 문제와 동일
function solution(routes) {
  var answer = 1;
  const sortedRoutes = routes.sort((a, b) => a[1] - b[1]);
  let camera = sortedRoutes[0][1];
  for (let i = 1; i < sortedRoutes.length; i++) {
    const [start, end] = sortedRoutes[i];
    if (start > camera) {
      console.log(start, camera);
      answer++;
      camera = end;
    }
  }
  return answer;
}
  • 풀이방법이 아예 동일해서 그냥 구현
  • 차량이 나가는 시점 기준으로 정렬하는 방식으로 접근한다면 풀이가 어렵지 않음
  • 나가는 시간순으로 정렬, 다음 값의 시작 지점이 기존 값의 끝나는 지점보다 더 낮은 경우, 그 지점의 끝나는 부분부터 값을 다시 잡기

머리 식히기 문제로 푼 백준 13987번 Six sides

const input = require("fs")
  .readFileSync("input.txt")
  .toString()
  .trim()
  .split("\n");
const arr1 = input.shift().split(" ").map(Number);
const arr2 = input.shift().split(" ").map(Number);
let cwin = 0;
let cdraw = 0;
for (let i = 0; i < arr1.length; i++) {
  for (let j = 0; j < arr2.length; j++) {
    if (arr1[i] > arr2[j]) cwin++;
    else if (arr1[i] === arr2[j]) cdraw++;
  }
}
console.log((cwin / (36 - cdraw)).toFixed(5));
  • 구현은 간단한 주사위 승리 확률 추측
  • 내가 원하는건 ⇒ 승리했을 때 확률, 무승부는 제외
  • 그 말은 전체 경우의 수 중 무승부는 제외하고, 승리할 확률만 찾으면 됨.
  • 뇌정지오면 이런거 꼭 못품; 주기적으로 문제를 풀어 익숙해지자