코딩 테스트 공부

[백준 1043] 거짓말 (자바스크립트)

개발새발개발자_ 2025. 2. 27. 18:14

문제

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게 과장해서 말한다. 당연히 과장해서 이야기하는 것이 훨씬 더 재미있기 때문에, 되도록이면 과장해서 이야기하려고 한다. 하지만, 지민이는 거짓말쟁이로 알려지기는 싫어한다. 문제는 몇몇 사람들은 그 이야기의 진실을 안다는 것이다. 따라서 이런 사람들이 파티에 왔을 때는, 지민이는 진실을 이야기할 수 밖에 없다. 당연히, 어떤 사람이 어떤 파티에서는 진실을 듣고, 또다른 파티에서는 과장된 이야기를 들었을 때도 지민이는 거짓말쟁이로 알려지게 된다. 지민이는 이런 일을 모두 피해야 한다.

사람의 수 N이 주어진다. 그리고 그 이야기의 진실을 아는 사람이 주어진다. 그리고 각 파티에 오는 사람들의 번호가 주어진다. 지민이는 모든 파티에 참가해야 한다. 이때, 지민이가 거짓말쟁이로 알려지지 않으면서, 과장된 이야기를 할 수 있는 파티 개수의 최댓값을 구하는 프로그램을 작성하시오.

입력

첫째 줄에 사람의 수 N과 파티의 수 M이 주어진다.

둘째 줄에는 이야기의 진실을 아는 사람의 수와 번호가 주어진다. 진실을 아는 사람의 수가 먼저 주어지고 그 개수만큼 사람들의 번호가 주어진다. 사람들의 번호는 1부터 N까지의 수로 주어진다.

셋째 줄부터 M개의 줄에는 각 파티마다 오는 사람의 수와 번호가 같은 방식으로 주어진다.

N, M은 50 이하의 자연수이고, 진실을 아는 사람의 수는 0 이상 50 이하의 정수, 각 파티마다 오는 사람의 수는 1 이상 50 이하의 정수이다.

출력

첫째 줄에 문제의 정답을 출력한다.

 

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

 


#풀이 과정

진실을 알고 있는 사람들과 같은 파티에 참석한 사람들은 진실을 알게 된다.

그래서 진실을 알고 있는 사람들 집합을 만들고 진실을 알고 있는 사람들과 같은 파티에 참석한 사람들도 해당 집합에 추가하면, 그 외의 사람들은 진실을 알지 못하니 그들이 참석한 파티에서는 거짓을 말해도 된다.

 

따라서 아래와 같은 과정으로 풀었다.

1. 입력 받은 값들로 진실을 알고 있는 사람들 배열과 각 파티에 참가한 사람들 배열을 받는다.

2. 각 파티에 참가한 사람들 배열을 첫 번째 사람을 루트 노드로 한 집합으로 만든다. 이렇게 되면 모든 값들은 루트 노드가 생긴다.

3. 진실을 알고 있는 사람들 배열에서 각 값의 루트 노드를 찾아 진실을 아는 사람들 집합에 추가한다.

4. 각 파티의 루트 노드와 진실을 아는 사람들 집합을 비교해 집합 안에 없다면 진실을 모르는 사람들이므로 거짓을 말할 수 있다.

 

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

class UnionFind {
  constructor(n) {
    // 1부터 n까지 자기 자신을 부모로 하는 배열 생성
    this.parent = Array(n + 1)
      .fill(0)
      .map((_, i) => i);
  }

  // 루트 노드 찾기
  find(x) {
    // x의 부모가 자기자신이 아니면 재귀함수 => 부모 노드 찾아서 반환
    if (this.parent[x] !== x) {
      this.parent[x] = this.find(this.parent[x]);
    }
    return this.parent[x];
  }

  // 각 집합 합치기
  union(a, b) {
    const rootA = this.find(a);
    const rootB = this.find(b);
    
    // rootB의 부모 노드를 rootA로 변경
    if (rootA !== rootB) {
      this.parent[rootB] = rootA;
    }
  }
}

// 사람 수와 파티 수 입력
const [N, M] = input[0].split(" ").map(Number);

// 진실을 아는 사람의 수와 번호 입력
const truthInput = input[1].split(" ").map(Number);

// 진실을 아는 사람들의 배열 추출
const truthPeople = truthInput.slice(1);

// 각 파티의 참가자 수와 번호 입력
const partiesPeople = [];
for (i = 2; i < M + 2; i++) {
  const partiesInput = input[i].split(" ").map(Number);

  // 각 파티의 참가자 배열 추출
  partiesPeople.push(partiesInput.slice(1));
}

const uf = new UnionFind(N);

// 같은 파티에 있는 사람들을 하나의 집합으로 합침
for (const party of partiesPeople) {
  // 파티의 첫 번째 사람을 기준으로 합침
  for (let i = 1; i < party.length; i++) {
    uf.union(party[0], party[i]);
  }
}

// 진실을 아는 사람들의 집합의 루트 노드 구하기
const truthRoots = new Set();

for (const person of truthPeople) {
  truthRoots.add(uf.find(person));
}

let safeParty = 0;

// 루트 노드가 진실을 아는 사람들의 집합에 없다면 안전한 파티
for (const party of partiesPeople) {
  const partyRoot = uf.find(party[0]);

  if (!truthRoots.has(partyRoot)) {
    safeParty++;
  }
}

console.log(safeParty);