티스토리 뷰
문제 설명
프로그래머스
SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
n명의 권투선수가 권투 대회에 참여했고 각각 1번부터 n번까지 번호를 받았습니다. 권투 경기는 1대1 방식으로 진행이 되고, 만약 A 선수가 B 선수보다 실력이 좋다면 A 선수는 B 선수를 항상 이깁니다. 심판은 주어진 경기 결과를 가지고 선수들의 순위를 매기려 합니다. 하지만 몇몇 경기 결과를 분실하여 정확하게 순위를 매길 수 없습니다.
선수의 수 n, 경기 결과를 담은 2차원 배열 results가 매개변수로 주어질 때 정확하게 순위를 매길 수 있는 선수의 수를 return 하도록 solution 함수를 작성해주세요.
제한사항
- 선수의 수는 1명 이상 100명 이하입니다.
- 경기 결과는 1개 이상 4,500개 이하입니다.
- results 배열 각 행 [A, B]는 A 선수가 B 선수를 이겼다는 의미입니다.
- 모든 경기 결과에는 모순이 없습니다.
입출력 예시
n | results | return |
5 | [[4, 3], [4, 2], [3, 2], [1, 2], [2, 5]] | 2 |
입출력 예시 설명
- 2번 선수는 [1, 3, 4] 선수에게 패배했고 5번 선수에게 승리했기 때문에 4위입니다.
- 5번 선수는 4위인 2번 선수에게 패배했기 때문에 5위입니다.
풀이 과정
각자 진 사람 이긴 사람 따로 저장해 두어도 결국 그 자료들을 어떻게 써서 결과를 도출해야 할지 모르겠어서 검색해봤습니다.
플로이드 워셜 알고리즘이라고 합니다. 너무 오랜만에 들어서 반가울 정도입니다.
플로이드 워셜 알고리즘은 한 정점에서 다른 모든 정점들까지의 최단 거리를 구하는 다익스트라와 달리, 모든 정점 간의 최단 경로를 찾아주는 알고리즘입니다.
플로이드 워셜의 핵심 아이디어는 ‘a에서 b로 가는 최단 경로는, 중간에 c를 거치는 것이 더 빠를 수 있다’는 것입니다.
이 문제에서 어떻게 이 알고리즘을 적용할 수 있냐면,
결국 ‘정확하게 순위를 매길 수 있는 선수의 수’를 구해야 합니다.
여기서 ‘정확하게 순위를 매길 수 있다’가 어떤 것을 뜻할까요?
정확하게 순위를 매길 수 있으려면, 모든 사람과의 승패 결과를 알 수 있어야 합니다. 그런데 이 문제는 경기 결과 몇 개를 분실했죠. 심지어 누군가와는 경기를 치르지 않았을지도 모릅니다. 그러나 이 문제는 ‘A 선수가 B 선수보다 실력이 좋다면 A 선수는 B 선수를 항상 이긴다’라는 조건이 주어져 있습니다.
이것 때문에 우리는 1) a가 b를 이김 2) b가 c를 이김 이렇게 주어져도 경기를 치르지 않은 a와 c의 승패 결과도 추측할 수 있습니다. a > b > c 겠죠.
이렇게 a > b, b > c일 때, a > c이다. 를 예측하는 로직에서 플로이드 워셜 알고리즘이 쓰입니다.
이를 통해 주어진 데이터로 최대한의 승패를 예측하고, 그럼에도 둘 사이의 알 수 없는 승패 결과가 1개라도 존재한다면, 이 경우에는 순위를 매길 수 없게 됩니다.
이러한 로직을 바탕으로 아래와 같은 코드를 작성할 수 있습니다.
정답 코드
def solution(n, results):
answer = 0
# 모든 참가자들간의 승패를 저장하기 위해, n * n의 점수판을 0으로 초기화
# 이때, 승리 = 1, 패배 = -1, 알 수 없음 = 0 으로 표시
# scores[a][b]는 a가 b가 경기를 치뤘을 때 a가 -> b를 이겼는지, b에게 졌는지, 결과를 알 수 없는지를 뜻합니다.
scores = [[0] * n for _ in range(n)]
# results에 맞게 scores 배열을 채웁니다
for a,b in results:
# a가 b를 이김
scores[a-1][b-1] = 1
# b는 a에게 짐
scores[b-1][a-1] = -1
# 플로이드 워셜 알고리즘의 핵심 부분으로, 3개의 for문을 사용합니다
# k는 중간 지점을 뜻하게 됩니다
for k in range(n):
for i in range(n):
for j in range(n):
# i와 j가 같으면 자기자신이기 때문에 넘어갑니다
# 또는 i와 j의 경기 결과가 이미 승, 패로 저장이 되어 있다면 넘어갑니다
if i == j or scores[i][j] in [1, -1]:
continue
# i와 j의 경기 결과는 아직 모르지만
# 중간 지점인 k를 넣어서 살펴봤을 때
# i가 k를 이겼고, k가 j를 이겼다면,
# i > k and k > j => i > j 가 됩니다
if scores[i][k] == scores[k][j] == 1:
# i가 j를 이긴 것으로 표시
scores[i][j] = 1
# j는 i에게 졌고,
# k는 i에게 졌고,
# j는 k에게 진 것도 표시
scores[j][i] = scores[k][i] = scores[j][k] = -1
# 다 돌고 나서 행 단위로 살펴보았을 때
for row_score in scores:
# 그 행에서 0(= 알 수 없음)으로 표시된 값이 자기자신 1개라면
# 즉, 자기자신 제외 모두와의 경기 결과를 알고 있다면
if row_score.count(0) == 1:
# 순위를 알 수 있기 때문에 ans에 추가합니다
answer += 1
return answer
플로이드 워셜의 핵심 아이디어가 이렇게 적용됩니다.
플로이드 워셜의 시간 복잡도는 n번씩 도는 for문 3개니까 O(N^3)으로, 보통 100 이하일 때 자주 사용됩니다.
저는 애초에 ‘모든 사람과의 승패 결과’를 구해야 한다는 생각을 못 했는데, 이걸 구할 수 있어야만 문제의 조건에 맞게 카운트 되는 사람이니까 꼭 해야했던 생각입니다.
또한, 승리, 패배, 결과 알 수 없음을 1,-1,0으로 표현하는 것도 배울 점인 것 같습니다.
이상입니다.
'Algorithm' 카테고리의 다른 글
프로그래머스 Lv.1: 체육복 - Python 풀이 (0) | 2025.03.27 |
---|---|
백준 2167번: 2차원 배열의 합 (S5) - Python 풀이 (0) | 2025.03.26 |
백준 14503번: 로봇 청소기 (G5) - Python 풀이 (0) | 2025.03.13 |
프로그래머스 Lv.3: 대여 횟수가 많은 자동차들의 월별 대여 횟수 구하기 - MySQL 풀이 (0) | 2025.03.12 |
프로그래머스 Lv.3: 가장 먼 노드 - Python 풀이 (0) | 2025.03.11 |
- Total
- Today
- Yesterday
- 백트래킹
- 코테준비
- 알고리즘
- BFS
- ios
- Swift로백준풀기
- 구현
- Baekjoon
- SQL
- 이분탐색
- 코딩테스트
- Programmers
- Swift
- 스택
- 코테
- 다이나믹프로그래밍
- 힙
- 큐
- 투포인터
- 우선순위큐
- 프로그래머스
- ios앱개발
- 자료구조
- 그리디
- 백준
- 정렬
- swea
- Python
- dp
- MySQL
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |