[백준] #1043 거짓말

수영·2023년 2월 7일

백준

목록 보기
108/117
post-thumbnail

📌문제

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

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

입력

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

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

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

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

출력

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

예제 입력1

4 3
0
2 1 2
1 3
3 2 3 4

예제 출력1

3

예제 입력2

4 1
1 1
4 1 2 3 4

예제 출력2

4 1
1 1
4 1 2 3 4

예제 입력3

4 1
0
4 1 2 3 4

예제 출력3

1

예제 입력4

4 5
1 1
1 1
1 2
1 3
1 4
2 4 1

예제 출력4

2

백준 1043번 문제

💡Idea

지민이가 거짓말쟁이가 되지 않기 위해서는 아래와 같은 규칙을 지켜야 합니다.

  • 진실을 아는 사람들이 있을 때는 진실을 이야기한다.
  • 진실을 아는 사람들과 같은 파티에 간 사람들이 있을 때에도 진실을 이야기 한다.
  • 즉, 진실을 아는 사람들과 접점이 아예 없는 사람들이 있을 때에만 과장된 이야기를 할 수 있다.

위의 규칙을 들여다보면 사람들을 두 분류로 나눌 수 있습니다.

  1. 진실을 아는 사람들 + 그 사람들과 접점이 있는 사람들
  2. 진실을 아는 사람들과의 접점이 아예 없는 사람들

이렇게 서로 공통된 원소가 없는 두 집합을 만들기 위해서, Disjoint Set과 Union-Find를 사용하여 문제를 풀어보았습니다.

💻코드

  • ⏰ 시간 : 44 ms / 메모리 : 31256 KB
import sys
input = sys.stdin.readline

N, M = map(int, input().split())
knowing = set(list(map(int, input().split()))[1:])
parties = [list(map(int, input().split())) for _ in range(M)]

if len(knowing) == 0:
    print(M)
    exit()

parent = [i for i in range(N + 1)]
rank = [1 for _ in range(N + 1)]

def find(u):
    if parent[u] == u:
        return u
    parent[u] = find(parent[u])
    return parent[u]

def union(u, v):
    u = find(u)
    v = find(v)

    if u != v:
        if rank[u] > rank[v]:
            parent[u] = v
        elif rank[u] < rank[v]:
            parent[v] = u
        else:
            parent[v] = u
            rank[u] += 1

    if u in knowing or v in knowing:
        knowing.add(u)
        knowing.add(v)

for party in parties:
    for i in range(1, party[0]):
        union(party[i], party[i + 1])

ans = 0
for party in parties:
    if len([i for i in range(len(party) - 1) if find(party[i + 1]) in knowing]) == 0:
        ans += 1

print(ans)

📝코드 설명

변수

  • knowing : 진실을 알고 있는 사람들과 파티에서 진실을 들은 사람들의 집합
  • parties : 각 파티에 오는 사람들 수와 리스트
  • parent : 각자의 부모 리스트
  • rank : 트리의 높이를 저장하며, 두 트리를 합칠 때 항상 높이가 더 낮은 트리를 높은 트리 밑에 넣어 트리의 높이가 크게 높아지는 상황을 방지하기 위하여 사용

같은 파티에 오는 사람들끼리는 모두 union을 한 뒤, 각 파티에 오는 사람들을 확인하며 knowing에 포함되어 있는 사람들이 한 명도 없는 파티에서만 과장된 이야기를 할 수 있도록 구현하였습니다.

Reference

분리집합(Disjoint set)

profile
하고 싶은 건 그냥 죽도록 합니다

0개의 댓글