본문 바로가기
📊 Algorithm/Algorithm 주제별 정리

🧚알고리즘🧚-그래프- 위상 정렬

by 정람지 2023. 7. 1.

🧚위상 정렬🧚

사이클이 없는 방향 그래프에서 노드 순서를 찾는 알고리즘

시간 복잡도 : O(노드 수 + 에지 수)

 

사이클이 존재하면 안 됨 -> 노드 간의 순서를 명확하게 정의할 수 없기 때문

위상 정렬이 늘 같은 정렬 결과를 보장하지는 않음(위상이 같은 노드가 존재할 시)

 

진입 차수(in-degree) : 자기 자신을 가르키는 에지의 개수

 

1. 인접 리스트를 만든 후 이를 이용해 진입 차수 리스트를 만든다.
2. 진입 차수가 0인 노트를 선택하고
->  선택된 노드를 결과 리스트에 저장 / 인접 리스트에서 선택된 노드들의 진입 차수를 1씩 빼기
3. 모든 노드가 정렬될 때까지 2 반복하기. (0이 생기지 않는 경우 없음)

🥇골드 3

 

2252번: 줄 세우기

첫째 줄에 N(1 ≤ N ≤ 32,000), M(1 ≤ M ≤ 100,000)이 주어진다. M은 키를 비교한 회수이다. 다음 M개의 줄에는 키를 비교한 두 학생의 번호 A, B가 주어진다. 이는 학생 A가 학생 B의 앞에 서야 한다는 의

www.acmicpc.net

근본

from collections import deque
from sys import stdin
N, M = map(int, stdin.readline().split())
Nlist = [[] for _ in range(N+1)] #인접리스트
inDegree = [0 for _ in range(N+1)] #진입차수

for _ in range(M):
    a, b = map(int, stdin.readline().split())
    Nlist[a].append(b) # 인접 리스트 저장 (방향 O)
    inDegree[b] += 1 # 진입 차수 데이터 저장

# 큐 선입선출==위상정렬 굿  
MyQueue = deque()
for i in range(1,N+1):
    if inDegree[i] == 0: # 진입 차수 == 0 노드 큐 저장
        MyQueue.append(i)

# 큐 pop => 탐색결과 append, 해당 노드의 인접 노드 진입 차수 --
while(MyQueue):
    nowNode = MyQueue.popleft()
    print(nowNode, end = ' ')
    for cha in Nlist[nowNode]:
        inDegree[cha] -= 1
        if inDegree[cha] == 0:
            MyQueue.append(cha)

🥇골드 3

 

1516번: 게임 개발

첫째 줄에 건물의 종류 수 N(1 ≤ N ≤ 500)이 주어진다. 다음 N개의 줄에는 각 건물을 짓는데 걸리는 시간과 그 건물을 짓기 위해 먼저 지어져야 하는 건물들의 번호가 주어진다. 건물의 번호는 1부

www.acmicpc.net

분기가 갈라지므로 시간 계산을 어떻게 해야 하나 했는데

리스트 하나를 새로 만들어서 이전 부모 노드(nowNode)의 시간 + 지금 노드(cha) 로 저장했다.

from collections import deque
from sys import stdin
N= int(stdin.readline())
Nlist = [[] for _ in range(N+1)] #인접리스트
inDegree = [0 for _ in range(N+1)] #진입차수
result = [0 for _ in range(N+1)] #시간
reresult =[] #순서

for i in range(1,N+1):
    nlist = list(map(int, stdin.readline().split()))[0:-1]
    result[i] = nlist[0] #자체 시간
    for j in range(len(nlist)-1):
        Nlist[nlist[j+1]].append(i)
    inDegree[i] += len(nlist)-1 

MyQueue = deque()
for i in range(1,N+1):
    if inDegree[i] == 0: 
        MyQueue.append(i)

while(MyQueue):
    nowNode = MyQueue.popleft()
    reresult.append(nowNode)
    for cha in Nlist[nowNode]:
        inDegree[cha] -= 1
        result[cha] += result[nowNode]
        if inDegree[cha] == 0:
            MyQueue.append(cha)

for cha in reresult:
    print(result[cha])

????????

 

글 읽기 - 에엥 대체 왜 출력 초과가 나는 건가요..??

댓글을 작성하려면 로그인해야 합니다.

www.acmicpc.net

아 이런.. 사이클은 없지만^^ 경로는 여러개 있을 수 있기 때문에 부모 노드가 여러개 생길 수도 있다는 걸 알았어야지..

GG

책을 보도록 하겠다.

새로운 아이디어가

경로가 여러개라면 개중 가장 큰 것이 답이 된다. 결과 리스트를 하나 더 만들어보자

from collections import deque
from sys import stdin
N= int(stdin.readline())
Nlist = [[] for _ in range(N+1)] #인접리스트
inDegree = [0 for _ in range(N+1)] #진입차수
result = [0 for _ in range(N+1)] #건설기본시간
reresult =[] #순서
finalresult = [0 for _ in range(N+1)] #찐

for i in range(1,N+1):
    nlist = list(map(int, stdin.readline().split()))[0:-1]
    result[i] = nlist[0] #자체 시간
    for j in range(len(nlist)-1):
        Nlist[nlist[j+1]].append(i)
    inDegree[i] += len(nlist)-1 

MyQueue = deque()
for i in range(1,N+1):
    if inDegree[i] == 0: 
        MyQueue.append(i)
        finalresult[i] = result[i]

while(MyQueue):
    nowNode = MyQueue.popleft()
    reresult.append(nowNode)
    for cha in Nlist[nowNode]:
        inDegree[cha] -= 1
        finalresult[cha] = max(finalresult[cha],finalresult[nowNode]+result[cha])
        if inDegree[cha] == 0:
            MyQueue.append(cha)

for cha in reresult:
    print(finalresult[cha])

앗 이번엔 틀렸다!

이젠 안해

답지 본다

잠깐..

이거 출력을..건축순서 상관 없이 그냥 숫자크기대로 출력하는 거였나?

..

for i in range(1,N+1):
    print(finalresult[i])

마지막 이렇게 하면 맞았다

쓸데없는짓을햇군...

하필 테케 답 건축순서가 숫자크기순일건 뭐람 


✳️ 플래티넘 5

 

1948번: 임계경로

첫째 줄에 도시의 개수 n(1 ≤ n ≤ 10,000)이 주어지고 둘째 줄에는 도로의 개수 m(1 ≤ m ≤ 100,000)이 주어진다. 그리고 셋째 줄부터 m+2줄까지 다음과 같은 도로의 정보가 주어진다. 처음에는 도로의

www.acmicpc.net

지쳣기때문에..플레이기때문에..

그냥 코드 보고 열심히 공부하겠다

뇌피셜 과정 생략

import sys
from collections import deque
input = sys.stdin.readline

N = int(input())
M = int(input())
A = [[] for _ in range(N+1)]
reverseA = [[] for _ in range (N+1)]
indegree = [0]*(N+1)

# 진입 차수 리스트
for i in range(M):
    S, E, V = map(int, input().split())
    A[S].append((E, V))
    reverseA[E].append((S, V))# 역방향 에지 정보 저장
    indegree[E] += 1

startDosi, endDosi = map(int, input().split())

queue = deque()
queue.append(startDosi)
result = [0]*(N+1)

while queue: # 위상 정렬 수행
    now = queue.popleft()
    for next in A[now]:
        indegree[next[0]] -= 1
        result[next[0]] = max(result[next[0]], result[now] + next[1])
        if indegree[next[0]] == 0:
            queue.append(next[0])

resultCount = 0
visited = [False]*(N+1)
queue.clear()
queue.append(endDosi)
visited[endDosi] = True

while queue: # 위상 정렬 reverse 수행
    now = queue.popleft()
    for next in reverseA [now]:
        # 1분도 쉬지 않는 도로 체크
        if result[next[0]] + next[1] == result[now]:
            resultCount += 1
            if not visited [next[0]]:
                visited[next[0]] = True
                queue.append(next[0])

print(result[endDosi])
print(resultCount)

<에지 뒤집기> 아이디어 

: 에지의 방향이 반대인 역방향 인접 리스트 저장해서 이용

 

임계 경로 : 여러 가지 경로 중에서 가장 긴 시간이 걸리는 경로

(도착 도시의 임계 경로값이 만나는 시간이 됨)

(역방향 위상 정렬 수행 중 "해당 도시의 임계 경로값 + 도로시간(에지) == 이전 도시의 임계 경로값"일 경우 가 1분도 쉬지 않고 가야 하는 도로)

왜????

왜>?>???>이해 ㄴ

 

도착지점으로부터 "백트래킹" 하는 거래